ggpubr
options & settings
For scrollable output
Loading libs
# install.packages("gapminder")
library(tidyverse)
library(ggpubr)
library(gapminder)
creating theme_viny_bright
theme_viny_bright <- function(){
library(ggthemes)
ggthemes::theme_fivethirtyeight() %+replace%
theme(
axis.title = element_text(),
axis.text = element_text(size = 13),
legend.text = element_text(size = 10),
panel.background = element_rect(fill = "white"),
plot.background = element_rect(fill = "white"),
strip.background = element_blank(),
legend.background = element_rect(fill = NA),
legend.key = element_rect(fill = NA),
plot.title = element_text(hjust = 0.5,
size = 19,
face = "bold"),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
)
}
Loading gapminder lib data
Rows: 1,704
Columns: 6
$ country <fct> Afghanistan, Afghanistan, Afghanistan, Afghanistan, Afghanistan, Afghanistan, Afgh...
$ continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Europe, Eu...
$ year <int> 1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007, 1952, 1957...
$ lifeExp <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, 38.438, 39.854, 40.822, 41.674, 41.763, 42...
$ pop <int> 8425333, 9240934, 10267083, 11537966, 13079460, 14880372, 12881816, 13867957, 1631...
$ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, 739.9811, 786.1134, 978.0114, 852.3959, 64...
Boxplot within Violin & more
violin_plot1 <- gapminder %>% filter(year == max(year)) %>%
ggpubr::ggviolin(., x = "continent", y = "gdpPercap",
fill = "continent", color = "continent",
add = "boxplot", add.params = list(fill = "white", shape = "continent"))
set_palette(violin_plot1, palette = "jco")

violin_plot2 <- gapminder %>% filter(year == max(year)) %>%
ggpubr::ggviolin(., x = "continent", y = "gdpPercap",
color = "continent",
add = "point")
set_palette(violin_plot2, palette = "jco")

violin_plot3 <- gapminder %>% filter(year == max(year)) %>%
ggpubr::ggviolin(., x = "continent", y = "gdpPercap",
color = "continent",
add = "point")
set_palette(violin_plot3, palette = "jco")

violin_plot4 <- gapminder %>% filter(year == max(year)) %>%
ggpubr::ggviolin(., x = "continent", y = "gdpPercap",
color = "continent",
add = "jitter")
set_palette(violin_plot4, palette = "jco")

Loading latest gapminder data
gapminder_new <- read_csv("E:/3. R/gapminder data/income - gdppercap/gdppercapita_us_inflation_adjusted.csv")
head(gapminder_new)
[1] 191 61
gapminder_new %>% select(country,"2010","2014","2017","2019") %>% head()
gap_longer <- gapminder_new %>%
pivot_longer(cols = !country, names_to = "year", values_to = "gdpPercap")
gap_longer %>% head()
[1] 11460 3
gap_longer <- gap_longer %>% mutate(year = as.integer(year))
gap_longer %>% filter(year == max(year))
gap_longer %>% filter(year == max(year)) %>% count(country)
gap_longer %>% filter(year == max(year)) %>% count(country) %>% filter(n > 1)
gapminder %>% group_by(country, continent) %>% count()
gap_longer <- left_join(x = gap_longer,
y = gapminder %>%
group_by(country, continent) %>%
count() %>%
select(country, continent) ,
by = "country")
gap_longer %>% head()
[1] 11460 4
gap_longer$continent %>% levels()
[1] "Africa" "Americas" "Asia" "Europe" "Oceania"
gap_longer %>% filter(is.na(continent)) %>% select(country) %>% unique() %>% head()
fill_continents_df <- data.frame(
country = c("Andorra", "Antigua and Barbuda", "Armenia",
"Azerbaijan", "Bahamas", "Barbados", "Belarus",
"Belize", "Bhutan", "Brunei"),
continent = c("Europe", "Americas", "Europe", "Europe", "Americas",
"Americas", "Europe", "Americas", "Asia", "Asia")
)
fill_continents_df
top_30 <- gap_longer %>%
filter(!is.na(continent), year %in% c(2019)) %>%
top_n(n = 30, wt = gdpPercap) %>%
arrange(gdpPercap)
top_30 %>% head()
Publish ready bar plots
Plot from: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/
ggbarplot(top_30, x = "country", y = "gdpPercap", fill = "continent",
color = "white",
palette = "jco",
sort.val = "desc",
sort.by.groups = F,
x.text.angle = 90)

ggbarplot(top_30, x = "country", y = "gdpPercap", fill = "continent",
color = "white",
palette = "jco",
sort.val = "asc",
sort.by.groups = T,
x.text.angle = 90)

Publish ready lollypop plots
ggdotchart(top_30, x = "country", y = "gdpPercap",
color = "continent",
palette = "jco",
sort.val = "desc",
sort.by.groups = T,
add = "segments",
rotate = TRUE,
group = "continent",
dot.size = 6,
label = top_30$gdpPercap,
font.label = list(color = "black", size = 12, vjust = 0.5, hjust = -1),
ggtheme = theme_pubr()
)

ggdotchart(top_30, x = "country", y = "gdpPercap",
color = "continent",
palette = "jco",
sorting = "desc",
sort.by.groups = T,
add = "segments",
rotate = TRUE,
group = "continent",
dot.size = 9,
label = top_30$gdpPercap,
font.label = list(color = "black", size = 12, vjust = 0.5, hjust = -1),
ggtheme = theme_pubr()
)

from:
https://rpkgs.datanovia.com/ggpubr/reference/ggdotchart.html http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/
ggdotchart(top_30, x = "country", y = "gdpPercap",
color = "continent",
palette = "jco",
sorting = "desc",
sort.by.groups = T,
add = "segments",
# rotate = TRUE,
group = "continent",
dot.size = 9,
label = top_30$gdpPercap,
font.label = list(color = "black", size = 12, vjust = 0.5, hjust = -1),
ggtheme = theme_pubr(),
# x.text.col = TRUE
)

Highlight Plots
ggplot(data = gap_longer) +
geom_line(col = "grey", aes(x = year, y = gdpPercap, group = country)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5))

ggplot(data = gap_longer) +
geom_line(col = "grey", aes(x = year, y = gdpPercap, group = country)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10()

gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway","Denmark") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, group = country, col = highlight_type)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10() +
scale_colour_manual(values = c("grey", "black")) +
scale_size_manual(values = c(.5, 7))

gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, group = country, col = country)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10() +
gghighlight(highlight_type == "Yes") +
labs(title = "GDP/Capita (Income / Person) in $ of Countries wrt to time",
caption = "Data Source: Gapminder",
y = "Log of Income (GDP / Capita)"
)

Log scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10(labels = scales::comma) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Income(Log of GDP/Capita in $) of world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Log of Income (GDP / Capita) i.e Multiplier scale"
)

ggsave(filename = "gdpPerCap_log_highlighted_chi.jpg", dpi = 300, height = 8, width = 10)
Facet Log scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10(labels = scales::comma) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Income(Log of GDP/Capita in $) of world countries based on continents across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Log of Income (GDP / Capita) i.e Multiplier scale"
)

ggsave(filename = "gdpPerCap_log_highlighted_facet_chi.jpg", dpi = 300, height = 8, width = 10)
Normal scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_continuous(labels = scales::comma,
breaks = seq(from = 0, to = 200000, by = 25000)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Income(GDP/Capita in $) of world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Income (GDP / Capita) in $"
)

ggsave(filename = "gdpPerCap_norm_highlighted_chi.jpg", dpi = 300, height = 8, width = 10)
Facet Normal scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_continuous(labels = scales::comma,
breaks = seq(from = 0, to = 200000, by = 25000)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Income(GDP/Capita in $) of world countries based on continents across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Income (GDP / Capita) in $"
)

ggsave(filename = "gdpPerCap_norm_highlighted_facet_chi.jpg", dpi = 300, height = 8, width = 10)
Applying theme_viny_bright
Log scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10(labels = scales::comma) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("pink", 0.4))) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Income(Log of GDP/Capita in $) of world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Log of Income (GDP / Capita) i.e Multiplier scale"
)

ggsave(filename = "gdpPerCap_log_highlighted_pink.jpg", dpi = 300, height = 8, width = 10)
Facet Log scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_log10(labels = scales::comma) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("pink", 0.4))) +
facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Income(Log of GDP/Capita in $) of world countries based on continents across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Log of Income (GDP / Capita) i.e Multiplier scale"
)

ggsave(filename = "gdpPerCap_log_highlighted_facet_pink.jpg", dpi = 300, height = 8, width = 10)
Normal scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_continuous(labels = scales::comma,
breaks = seq(from = 0, to = 200000, by = 25000)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("pink", 0.4))) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Income(GDP/Capita in $) of world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Income (GDP / Capita) in $"
)

ggsave(filename = "gdpPerCap_norm_highlighted_pink.jpg", dpi = 300, height = 8, width = 10)
Facet Normal scale
gap_longer %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1960, to = 2020, by = 5)) +
scale_y_continuous(labels = scales::comma,
breaks = seq(from = 0, to = 200000, by = 25000)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("pink", 0.4))) +
facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Income(GDP/Capita in $) of world countries based on continents across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Income (GDP / Capita) in $"
)

ggsave(filename = "gdpPerCap_norm_highlighted_facet_pink.jpg", dpi = 300, height = 8, width = 10)
Loading tax data
Data source:
https://ourworldindata.org/taxation
tax_rev_to_gdp <- read_csv("E:/3. R/ourworldindata.org/total-tax-revenues-gdp.csv")
head(tax_rev_to_gdp)
tax_rev_to_gdp <- tax_rev_to_gdp %>%
rename("tax_revnue_perc_of_gdp" = "Total tax revenue (% of GDP) (ICTD (2019))",
"country" = "Entity",
"year" = "Year")
head(tax_rev_to_gdp)
Adding continent
tax_rev <- left_join(x = tax_rev_to_gdp,
y = gapminder %>%
group_by(country, continent) %>%
count() %>%
select(country, continent),
by = "country"
)
summary(tax_rev)
country Code year tax_revnue_perc_of_gdp continent
Length:5318 Length:5318 Min. :1980 Min. : 0.08581 Africa :1309
Class :character Class :character 1st Qu.:1993 1st Qu.:11.63091 Americas: 722
Mode :character Mode :character Median :2002 Median :18.01959 Asia : 749
Mean :2001 Mean :20.05868 Europe : 926
3rd Qu.:2009 3rd Qu.:27.63104 Oceania : 75
Max. :2017 Max. :56.91614 NA's :1537
tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = round(tax_revnue_perc_of_gdp,2), col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
scale_y_continuous(#labels = scales::percent,
breaks = seq(from = 0, to = 40, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
# facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Total Tax Revenue earned % of GDP for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)

tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = round(tax_revnue_perc_of_gdp,2), col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
scale_y_continuous(labels = label_dollar(prefix = "", suffix = " %"),
breaks = seq(from = 0, to = 40, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90),
legend.position = "none",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5, colour = "maroon")
) +
labs(title = "Total Tax Revenue earned % of GDP for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)
Error in label_dollar(prefix = "", suffix = " %") :
could not find function "label_dollar"
Applying theme_viny_bright
tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = round(tax_revnue_perc_of_gdp,2), col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
scale_y_continuous(labels = label_dollar(prefix = "", suffix = " %"),
breaks = seq(from = 0, to = 60, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("pink", 0.4))) +
# facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Total Tax Revenue earned % of GDP for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)

tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = round(tax_revnue_perc_of_gdp,2), col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
scale_y_continuous(labels = label_dollar(prefix = "", suffix = " %"),
breaks = seq(from = 0, to = 60, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("#ffaa33", 0.4))) +
facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Total Tax Revenue earned % of GDP for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)

user defined function
udf_tax_rev_plot <- function(background_line_color = grey){
background_line_color = enquo(background_line_color)
tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = round(tax_revnue_perc_of_gdp,2), col = country), size = 1.1) +
scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
scale_y_continuous(labels = label_dollar(prefix = "", suffix = " %"),
breaks = seq(from = 0, to = 60, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha(background_line_color, 0.4))) +
facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "Total Tax Revenue earned % of GDP for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)
}
Error: Unknown colour name: ~
udf_tax_rev_plot <- function(background_line_color = grey){
background_line_color = enquo(background_line_color)
print(as_label(background_line_color) )
}
udf_tax_rev_plot("#33ffff") #33ffff
[1] "\"#33ffff\""
country_highlight_plot <- function(df = gapminder, y_var = gdpPercap,
background_line_color = "grey",
countries = default_list
){
# default list of highlight countries
default_list = c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China")
# quoting y-axis variable
y_var = enquo(y_var)
# Data Prep.
df %>%
mutate(highlight_type = case_when(country %in% countries ~ "Yes",
TRUE ~ "No")) %>%
# Plotting
ggplot() +
geom_line(aes(x = year, y = round(!!y_var,2), col = country), size = 1.1) +
# scale_x_continuous(breaks = seq(from = 1980, to = 2020, by = 5)) +
# scale_y_continuous(labels = label_dollar(prefix = "", suffix = " %"),
# breaks = seq(from = 0, to = 60, by = 5)) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha(background_line_color, 0.4))) +
# facet_wrap(~continent) +
theme_viny_bright() +
theme(axis.text.x = element_text(angle = 90)
) +
labs(title = "GDP/Cap for world countries across time",
subtitle = "created by ViSa",
caption = "Data Source: Gapminder",
y = "Total Tax Revenue % of GDP"
)
}
country_highlight_plot(df = gap_longer, countries = c("Australia","Singapore"), background_line_color = "pink")

ggcharts lib for dumbell plot
getting issues with this
popeurope %>%
filter(country %in% c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China")) %>%
ggcharts::dumbbell_chart(x = country,
y1 = pop1952,
y2 = pop2007,
top_n = 10,
point_colors = c("lightgray","orange")
) +
scale_y_continuous(
limits = c(0, NA),
labels = function(x) paste(x, "Mn.")
) +
theme_ng()

dumbbell_chart(
data = popeurope,
x = country,
y1 = pop1952,
y2 = pop2007,
top_n = 10,
point_colors = c("lightgray", "#494F5C")
) +
labs(
x = NULL,
y = "Population",
title = "Europe's Largest Countries by Population in 2007"
) +
scale_y_continuous(
limits = c(0, NA),
labels = function(x) paste(x, "Mn.")
)

gapminder_new %>%
select(country, "2010", "2019") %>% head()
ggalt for dumbell plots
# devtools::install_github('bbc/bbplot')
library(ggalt)
library(bbplot)
gapminder_new %>%
select(country, "2010", "2019") %>%
mutate(gap = "2019" - "2010") %>% head()
Error: Problem with `mutate()` input `gap`.
x non-numeric argument to binary operator
i Input `gap` is `"2019" - "2010"`.
Run `rlang::last_error()` to see where the error occurred.
gapminder %>%
filter(year == 1967 | year == 2007) %>%
select(country, year, lifeExp) %>%
spread(year, lifeExp) %>%
mutate(gap = `2007` - `1967`) %>%
arrange(desc(gap)) %>%
head(10)
gapminder_new %>%
filter(country %in% c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China")) %>%
select(country, "2010", "2019") %>%
mutate(gap = .[["2019"]] - .[["2010"]]) %>%
arrange(desc(gap)) %>%
ggplot(aes(x = .[["2010"]], xend = .[["2019"]],
y = reorder(country, gap), group = country)) +
geom_dumbbell(colour = "#dddddd", size = 2,
colour_x = "grey", colour_xend = "#FAAB18") +
bbc_style()

gapminder_new %>%
filter(country %in% c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China")) %>%
select(country, "1970", "2019") %>%
mutate(gap = .[["2019"]] - .[["1970"]]) %>%
arrange(desc(gap)) %>%
ggplot(aes(x = .[["1970"]], xend = .[["2019"]],
y = reorder(country, gap), group = country)) +
geom_dumbbell(colour = "#dddddd", size = 2,
colour_x = "grey", colour_xend = "#FAAB18") +
bbc_style()

gap_longer %>%
group_by(continent) %>%
filter(country == top_n(x=.,n=5,wt=gdpPercap) %>% select(country))
Error: Problem with `filter()` input `..1`.
x Input `..1` must be of size 2940 or 1, not size 30.
i Input `..1` is `country == top_n(x = ., n = 5, wt = gdpPercap) %>% select(country)`.
i The error occurred in group 1: continent = "Africa".
Run `rlang::last_error()` to see where the error occurred.
Below code gives top 5 countries by gdpPercap across the years
Overall_top5 <- gap_longer %>%
group_by(continent, country) %>%
summarise(gdpPercap = max(gdpPercap, na.rm = T)) %>%
top_n(n = 5, wt = gdpPercap) %>%
ungroup()
Overall_top5
Below code gives top 5 countries by gdpPercap of each continent across the years
gap_longer %>%
filter(
country %in% (Overall_top5 %>% pull(country))) %>%
ggplot(aes(x = year, y = gdpPercap, color = country)) +
geom_line() +
facet_wrap(~continent) +
theme_viny_bright() +
theme(legend.position = "none",
axis.text.x = element_text(angle = 90))

Overall_top5 %>% select(country)
gap_longer %>%
filter(year == 2019,
country %in% (Overall_top5 %>% pull(country)))
gap_longer %>%
filter(year == 2019,
country %in% Overall_top5$country )
gap_longer %>%
filter(
year == 2019,
country %in% (Overall_top5 %>% pull(country))
)
gap_longer %>%
filter(year == 2019, country %in% Overall_top5)
gap_longer %>%
filter(year == 2019, country %in% Overall_top5) %>%
pull(country)
character(0)
gap_longer %>%
filter(year == 2019, country %in% Overall_top5$country) %>%
select(country)
top5_by_continents <- gap_longer %>%
filter(year == 2019, country %in% Overall_top5$country) %>%
pull(country)
top5_by_continents
[1] "Australia" "Botswana" "Canada" "Chile"
[5] "Denmark" "Equatorial Guinea" "Gabon" "Ireland"
[9] "Israel" "Japan" "Kuwait" "Libya"
[13] "Liechtenstein" "Luxembourg" "Mauritius" "Monaco"
[17] "New Zealand" "Norway" "San Marino" "Saudi Arabia"
[21] "Singapore" "Sweden" "Switzerland" "Trinidad and Tobago"
[25] "United Arab Emirates" "United States" "Venezuela"
gap_longer %>%
mutate(highlight_type = case_when(country %in% top5_by_continents ~ "Yes",
TRUE ~ "No")) %>%
#filter(!is.na(continent)) %>% filter(!is.na(gdpPercap)) %>% #head()
na.exclude %>%
ggplot() +
geom_line(aes(x = year, y = gdpPercap, col = country), size = 1.1) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60),
legend.position = "none")

tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
ggplot() +
geom_line(aes(x = year, y = tax_revnue_perc_of_gdp, col = as.factor(country)), size = 1.1) +
gghighlight(highlight_type == "Yes",
unhighlighted_params = list(size = 1, colour = alpha("grey", 0.4))) +
facet_wrap(~continent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 60),
legend.position = "none")

gap_longer %>%
mutate(highlight_type = case_when(country %in% top5_by_continents ~ "Yes",
TRUE ~ "No")) %>%
#filter(!is.na(continent)) %>% filter(!is.na(gdpPercap)) %>% #head()
na.exclude %>% summary()
country year gdpPercap continent highlight_type
Length:6700 Min. :1960 Min. : 132 Africa :2473 Length:6700
Class :character 1st Qu.:1978 1st Qu.: 910 Americas:1403 Class :character
Mode :character Median :1993 Median : 3135 Asia :1318 Mode :character
Mean :1992 Mean : 9938 Europe :1396
3rd Qu.:2006 3rd Qu.:12400 Oceania : 110
Max. :2019 Max. :92600
tax_rev %>%
mutate(highlight_type = case_when(country %in%
c("India","Singapore","Malaysia","Norway",
"Denmark","United States","United Kingdom","China") ~ "Yes",
TRUE ~ "No")) %>%
summary()
country Code year tax_revnue_perc_of_gdp continent
Length:5318 Length:5318 Min. :1980 Min. : 0.08581 Africa :1309
Class :character Class :character 1st Qu.:1993 1st Qu.:11.63091 Americas: 722
Mode :character Mode :character Median :2002 Median :18.01959 Asia : 749
Mean :2001 Mean :20.05868 Europe : 926
3rd Qu.:2009 3rd Qu.:27.63104 Oceania : 75
Max. :2017 Max. :56.91614 NA's :1537
highlight_type
Length:5318
Class :character
Mode :character
plotly highlight charts
from:
https://stackoverflow.com/questions/64385240/is-there-a-way-to-have-a-highlighted-chart-as-well-as-have-interactivity-of-sele?noredirect=1#comment113856801_64385240
https://plotly-r.com/client-side-linking.html#filter
# from ggplot2 library
data("txhousing")
Rows: 8,602
Columns: 9
$ city <chr> "Abilene", "Abilene", "Abilene", "Abilene", "Abilene", "Abilene", "Abilene", "Abil...
$ year <int> 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2001, 2001...
$ month <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2...
$ sales <dbl> 72, 98, 130, 98, 141, 156, 152, 131, 104, 101, 100, 92, 75, 112, 118, 105, 150, 13...
$ volume <dbl> 5380000, 6505000, 9285000, 9730000, 10590000, 13910000, 12635000, 10710000, 761500...
$ median <dbl> 71400, 58700, 58100, 68600, 67300, 66900, 73500, 75000, 64500, 59300, 70900, 65000...
$ listings <dbl> 701, 746, 784, 785, 794, 780, 742, 765, 771, 764, 721, 658, 779, 700, 738, 810, 77...
$ inventory <dbl> 6.3, 6.6, 6.8, 6.9, 6.8, 6.6, 6.2, 6.4, 6.5, 6.6, 6.2, 5.7, 6.8, 6.0, 6.4, 7.0, 6....
$ date <dbl> 2000.000, 2000.083, 2000.167, 2000.250, 2000.333, 2000.417, 2000.500, 2000.583, 20...
tx <- highlight_key(txhousing, ~city)
base <- plot_ly(tx, color = I("grey")) %>%
group_by(city)
time_series <- base %>%
group_by(city) %>%
add_lines(x = ~date, y = ~median)
highlight(
time_series,
on = "plotly_hover",
selectize = FALSE,
dynamic = FALSE,
color = "red",
persistent = FALSE
)
Error in dirname(to) : path too long
plotly_1 <- gap_longer %>%
filter(continent == "Asia") %>%
na.exclude() %>%
plotly::highlight_key(., ~country)
base <- plot_ly(plotly_1, color = I("grey")) %>%
group_by(country)
time_series <- base %>%
group_by(country) %>%
add_lines(x = ~year, y = ~gdpPercap)
highlight(
time_series,
on = "plotly_hover",
selectize = FALSE,
dynamic = FALSE,
color = "red",
persistent = FALSE
)
Error in dirname(to) : path too long
plotly_1 <- gap_longer %>%
filter(continent == "Asia", gdpPercap < max(20000)) %>%
na.exclude() %>%
plotly::highlight_key(., ~country)
base <- plot_ly(plotly_1, color = I("grey")) %>%
group_by(country)
tme_series <- base %>%
group_by(country) %>%
add_lines(x = ~year, y = ~gdpPercap)
highlight(
time_series,
on = "plotly_click",
selectize = TRUE,
dynamic = TRUE,
color = "red",
persistent = TRUE
)
Error in dirname(to) : path too long
hist <- add_histogram(
base,
x = ~gdpPercap,
histnorm = "probability density"
)
subplot(time_series, hist, nrows = 2) %>%
layout(barmode = "overlay", showlegend = FALSE) %>%
highlight(
dynamic = TRUE,
selectize = TRUE,
color = "red",
selected = attrs_selected(opacity = 0.3)
)
Error in dirname(to) : path too long
Trying to subtract row wise
gapminder_new %>%
filter(country == c("India","Bangladesh"))
failed attempts
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
.[["India",]] - .[["Bangladesh",]]
Error: Subscript can't be missing for tibbles in `[[`.
Run `rlang::last_error()` to see where the error occurred.
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
.["India",] - .["Bangladesh",]
Error: object '.' not found
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
.[country == "India",] - .[country == "Bangladesh",]
Error: Must subset rows with a valid subscript vector.
i Logical subscripts must match the size of the indexed input.
x Input has size 2 but subscript `country == "India"` has size 1704.
Run `rlang::last_error()` to see where the error occurred.
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
.[India,] - .[Bangladesh,]
Error in `[.tbl_df`(., India, ) : object 'India' not found
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
.[[India,]] - .[[Bangladesh,]]
Error: Subscript can't be missing for tibbles in `[[`.
Run `rlang::last_error()` to see where the error occurred.
gapminder_new %>%
filter(country == c("India","Bangladesh")) %>%
mutate(Diff_result = (. %>% filter(country == "India") ) - (. %>% filter(country == "Bangladesh")) )
Error: Problem with `mutate()` input `Diff_result`.
x non-numeric argument to binary operator
i Input `Diff_result` is `-...`.
Run `rlang::last_error()` to see where the error occurred.
gapminder_new[country == "India",] - gapminder_new[country == "Bangladesh",]
Error: Must subset rows with a valid subscript vector.
i Logical subscripts must match the size of the indexed input.
x Input has size 191 but subscript `country == "India"` has size 1704.
Run `rlang::last_error()` to see where the error occurred.
gapminder_new[India,] - gapminder_new[Bangladesh,]
Error in `[.tbl_df`(gapminder_new, India, ) : object 'India' not found
gapminder_new %>%
filter(country == "India")
rownames(gapminder_new[country == "India",])
Error: Must subset rows with a valid subscript vector.
i Logical subscripts must match the size of the indexed input.
x Input has size 191 but subscript `country == "India"` has size 1704.
Run `rlang::last_error()` to see where the error occurred.
rownames(gapminder_new[country == India,])
Error in `[.tbl_df`(gapminder_new, country == India, ) :
object 'India' not found
gapminder_new[country == "India",]
Error: Must subset rows with a valid subscript vector.
i Logical subscripts must match the size of the indexed input.
x Input has size 191 but subscript `country == "India"` has size 1704.
Run `rlang::last_error()` to see where the error occurred.
rownames(gapminder_new) <- gapminder_new$country
gapminder_new["India", 2: ncol(gapminder_new)] #- gapminder_new["Bangladesh", ]
gapminder_new[, 2:ncol(gapminder_new)]
My own solution
test_set <- as.data.frame(gapminder_new)
rownames(test_set) <- test_set$country
head(test_set)
test_set %>% select(-country)
test_set["India",2:ncol(test_set)] - test_set["Bangladesh",2:ncol(test_set)]
LS0tDQp0aXRsZTogImdncHViciwgZ2FwbWluZGVyLCB0YXggaGlnaGxpZ2h0IHBsb3RzIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiANCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogICAgdG9jX2RlcHRoOiA2DQotLS0NCg0KDQojIGdncHVicg0KDQojIyBvcHRpb25zICYgc2V0dGluZ3MNCg0KRm9yIHNjcm9sbGFibGUgb3V0cHV0DQoNCmBgYHtjc3MsIGVjaG89RkFMU0V9DQouc2Nyb2xsLTEwMCB7DQogIG1heC1oZWlnaHQ6IDEwMHB4Ow0KICBvdmVyZmxvdy15OiBhdXRvOw0KICBiYWNrZ3JvdW5kLWNvbG9yOiBpbmhlcml0ZWQ7DQp9DQoNCg0KaDEsICNUT0M+dWw+bGkgew0KICBjb2xvcjogI0I2NEQzQTsNCn0NCg0KaDIsICNUT0M+dWw+dWw+bGkgew0KICBjb2xvcjogIzAwMDAwMDsNCn0NCg0KaDMsICNUT0M+dWw+dWw+dWw+bGkgew0KICBjb2xvcjogIzY0M2NiMjsNCn0NCg0KaDQsICNUT0M+dWw+dWw+dWw+dWw+bGkgew0KICBjb2xvcjogI2FlMDA1ODsNCn0NCg0KaDUsICNUT0M+dWw+dWw+dWw+dWw+dWw+bGkgew0KICBjb2xvcjogI2ZmYTQ0NzsNCn0NCg0KaDYsICNUT0M+dWw+dWw+dWw+dWw+dWw+dWw+bGkgew0KICBjb2xvcjogI0RBRTNEOTsNCn0NCg0KDQpgYGANCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGRwaSA9IDMwMCwgb3V0LndpZHRoID0gIjEwMCUiLGF0dHIub3V0cHV0PSdzdHlsZT0ibWF4LWhlaWdodDogMzAwcHg7IicpDQpgYGANCg0KDQpgYGB7cn0NCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCiMjIFNvdXJjZQ0KDQpodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL2FydGljbGVzLzI0LWdncHVici1wdWJsaWNhdGlvbi1yZWFkeS1wbG90cy8NCmh0dHBzOi8veW91dHUuYmUvTkpxdVVLYXZBT0kNCmh0dHBzOi8veW91dHUuYmUvVXZSYU5KQzA1RWMNCg0KDQojIyBMb2FkaW5nIGxpYnMNCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImdhcG1pbmRlciIpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGdhcG1pbmRlcikNCmBgYA0KDQojIyBjcmVhdGluZyB0aGVtZV92aW55X2JyaWdodA0KDQpgYGB7cn0NCnRoZW1lX3ZpbnlfYnJpZ2h0IDwtIGZ1bmN0aW9uKCl7DQogIA0KICBsaWJyYXJ5KGdndGhlbWVzKQ0KICANCiAgZ2d0aGVtZXM6OnRoZW1lX2ZpdmV0aGlydHllaWdodCgpICUrcmVwbGFjZSUNCiAgDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoKSwNCiAgICANCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwNCiAgICANCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLA0KICAgIA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLA0KICAgIA0KICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksDQogICAgDQogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICANCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpLA0KICAgIA0KICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9IE5BKSwNCg0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwNCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBjb2xvdXIgPSAibWFyb29uIikNCiAgICAgICkNCiAgDQogIH0NCmBgYA0KDQoNCiMjIExvYWRpbmcgZ2FwbWluZGVyIGxpYiBkYXRhDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBoZWFkKCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBnbGltcHNlKCkNCmBgYA0KDQpgYGB7cn0NCmF0dGFjaChnYXBtaW5kZXIpDQpgYGANCg0KDQojIyMgQm94cGxvdCB3aXRoaW4gVmlvbGluICYgbW9yZSANCg0KYGBge3J9DQp2aW9saW5fcGxvdDEgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JSANCiAgDQogIGdncHVicjo6Z2d2aW9saW4oLiwgeCA9ICJjb250aW5lbnQiLCB5ID0gImdkcFBlcmNhcCIsIA0KICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiY29udGluZW50IiwgY29sb3IgPSAiY29udGluZW50IiwgDQogICAgICAgICAgICAgICAgICAgYWRkID0gImJveHBsb3QiLCBhZGQucGFyYW1zID0gbGlzdChmaWxsID0gIndoaXRlIiwgc2hhcGUgPSAiY29udGluZW50IikpDQoNCnNldF9wYWxldHRlKHZpb2xpbl9wbG90MSwgcGFsZXR0ZSA9ICJqY28iKQ0KYGBgDQoNCg0KYGBge3J9DQp2aW9saW5fcGxvdDIgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JSANCiAgDQogIGdncHVicjo6Z2d2aW9saW4oLiwgeCA9ICJjb250aW5lbnQiLCB5ID0gImdkcFBlcmNhcCIsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImNvbnRpbmVudCIsIA0KICAgICAgICAgICAgICAgICAgIGFkZCA9ICJwb2ludCIpDQoNCnNldF9wYWxldHRlKHZpb2xpbl9wbG90MiwgcGFsZXR0ZSA9ICJqY28iKQ0KYGBgDQoNCg0KYGBge3J9DQp2aW9saW5fcGxvdDMgPC0gZ2FwbWluZGVyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JSANCiAgDQogIGdncHVicjo6Z2d2aW9saW4oLiwgeCA9ICJjb250aW5lbnQiLCB5ID0gImdkcFBlcmNhcCIsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImNvbnRpbmVudCIsIA0KICAgICAgICAgICAgICAgICAgIGFkZCA9ICJwb2ludCIpDQoNCnNldF9wYWxldHRlKHZpb2xpbl9wbG90MywgcGFsZXR0ZSA9ICJqY28iKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnZpb2xpbl9wbG90NCA8LSBnYXBtaW5kZXIgJT4lIGZpbHRlcih5ZWFyID09IG1heCh5ZWFyKSkgJT4lIA0KICANCiAgZ2dwdWJyOjpnZ3Zpb2xpbiguLCB4ID0gImNvbnRpbmVudCIsIHkgPSAiZ2RwUGVyY2FwIiwgDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiY29udGluZW50IiwgDQogICAgICAgICAgICAgICAgICAgYWRkID0gImppdHRlciIpDQoNCnNldF9wYWxldHRlKHZpb2xpbl9wbG90NCwgcGFsZXR0ZSA9ICJqY28iKQ0KYGBgDQoNCiMjIExvYWRpbmcgbGF0ZXN0IGdhcG1pbmRlciBkYXRhDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ldyA8LSByZWFkX2NzdigiRTovMy4gUi9nYXBtaW5kZXIgZGF0YS9pbmNvbWUgLSBnZHBwZXJjYXAvZ2RwcGVyY2FwaXRhX3VzX2luZmxhdGlvbl9hZGp1c3RlZC5jc3YiKQ0KDQpoZWFkKGdhcG1pbmRlcl9uZXcpDQpgYGANCg0KDQpgYGB7cn0NCmRpbShnYXBtaW5kZXJfbmV3KQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSBzZWxlY3QoY291bnRyeSwiMjAxMCIsIjIwMTQiLCIyMDE3IiwiMjAxOSIpICU+JSBoZWFkKCkNCmBgYA0KDQpgYGB7cn0NCmdhcF9sb25nZXIgPC0gZ2FwbWluZGVyX25ldyAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gIWNvdW50cnksIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAiZ2RwUGVyY2FwIikgDQoNCmdhcF9sb25nZXIgJT4lIGhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0KZGltKGdhcF9sb25nZXIpDQpgYGANCg0KYGBge3J9DQpnYXBfbG9uZ2VyIDwtIGdhcF9sb25nZXIgJT4lIG11dGF0ZSh5ZWFyID0gYXMuaW50ZWdlcih5ZWFyKSkNCmBgYA0KDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpIA0KYGBgDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JSBjb3VudChjb3VudHJ5KQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSBmaWx0ZXIoeWVhciA9PSBtYXgoeWVhcikpICU+JSBjb3VudChjb3VudHJ5KSAlPiUgZmlsdGVyKG4gPiAxKQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBncm91cF9ieShjb3VudHJ5LCBjb250aW5lbnQpICU+JSBjb3VudCgpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ2FwX2xvbmdlciA8LSBsZWZ0X2pvaW4oeCA9IGdhcF9sb25nZXIsIA0KICAgICAgICAgICAgIHkgPSBnYXBtaW5kZXIgJT4lIA0KICAgICAgICAgICAgICAgZ3JvdXBfYnkoY291bnRyeSwgY29udGluZW50KSAlPiUgDQogICAgICAgICAgICAgICBjb3VudCgpICU+JSANCiAgICAgICAgICAgICAgIHNlbGVjdChjb3VudHJ5LCBjb250aW5lbnQpICwNCiAgICAgICAgICAgICBieSA9ICAiY291bnRyeSIpIA0KDQpnYXBfbG9uZ2VyICU+JSBoZWFkKCkNCmBgYA0KDQoNCg0KYGBge3J9DQpkaW0oZ2FwX2xvbmdlcikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2FwX2xvbmdlciRjb250aW5lbnQgJT4lIGxldmVscygpDQpgYGANCg0KDQpgYGB7cn0NCmdhcF9sb25nZXIgJT4lIGZpbHRlcihpcy5uYShjb250aW5lbnQpKSAlPiUgc2VsZWN0KGNvdW50cnkpICU+JSB1bmlxdWUoKSAlPiUgIGhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0KZmlsbF9jb250aW5lbnRzX2RmIDwtIGRhdGEuZnJhbWUoDQogIA0KICBjb3VudHJ5ID0gYygiQW5kb3JyYSIsICJBbnRpZ3VhIGFuZCBCYXJidWRhIiwgIkFybWVuaWEiLCAJCQkJDQogICAgICAgICAgICAgICJBemVyYmFpamFuIiwJIkJhaGFtYXMiLCAiQmFyYmFkb3MiLCAiQmVsYXJ1cyIsIA0KICAgICAgICAgICAgICAiQmVsaXplIiwgIkJodXRhbiIsCSJCcnVuZWkiKSwNCg0KICBjb250aW5lbnQgPSBjKCJFdXJvcGUiLCAiQW1lcmljYXMiLCAiRXVyb3BlIiwgIkV1cm9wZSIsICJBbWVyaWNhcyIsIA0KICAgICAgICAgICAgICAgICJBbWVyaWNhcyIsICJFdXJvcGUiLCAiQW1lcmljYXMiLCAiQXNpYSIsICJBc2lhIikNCikNCg0KZmlsbF9jb250aW5lbnRzX2RmDQoNCmBgYA0KDQoNCg0KYGBge3J9DQp0b3BfMzAgPC0gZ2FwX2xvbmdlciAlPiUgDQogIGZpbHRlcighaXMubmEoY29udGluZW50KSwgeWVhciAlaW4lIGMoMjAxOSkpICU+JSANCiAgdG9wX24obiA9IDMwLCB3dCA9IGdkcFBlcmNhcCkgJT4lIA0KICBhcnJhbmdlKGdkcFBlcmNhcCkNCg0KdG9wXzMwICU+JSAgaGVhZCgpDQpgYGANCg0KIyMjIFB1Ymxpc2ggcmVhZHkgYmFyIHBsb3RzDQoNClBsb3QgZnJvbTogaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC9hcnRpY2xlcy8yNC1nZ3B1YnItcHVibGljYXRpb24tcmVhZHktcGxvdHMvDQoNCg0KYGBge3J9DQpnZ2JhcnBsb3QodG9wXzMwLCB4ID0gImNvdW50cnkiLCB5ID0gImdkcFBlcmNhcCIsIGZpbGwgPSAiY29udGluZW50IiwNCiAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCANCiAgICAgICAgICBzb3J0LnZhbCA9ICJkZXNjIiwgDQogICAgICAgICAgc29ydC5ieS5ncm91cHMgPSBGLCANCiAgICAgICAgICB4LnRleHQuYW5nbGUgPSA5MCkNCmBgYA0KDQpgYGB7cn0NCmdnYmFycGxvdCh0b3BfMzAsIHggPSAiY291bnRyeSIsIHkgPSAiZ2RwUGVyY2FwIiwgZmlsbCA9ICJjb250aW5lbnQiLA0KICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICBwYWxldHRlID0gImpjbyIsIA0KICAgICAgICAgIHNvcnQudmFsID0gImFzYyIsIA0KICAgICAgICAgIHNvcnQuYnkuZ3JvdXBzID0gVCwgDQogICAgICAgICAgeC50ZXh0LmFuZ2xlID0gOTApDQpgYGANCg0KIyMjIFB1Ymxpc2ggcmVhZHkgbG9sbHlwb3AgcGxvdHMNCg0KYGBge3IgZmlnLmhlaWdodD05fQ0KZ2dkb3RjaGFydCh0b3BfMzAsIHggPSAiY291bnRyeSIsIHkgPSAiZ2RwUGVyY2FwIiwNCiAgICAgICAgICBjb2xvciA9ICJjb250aW5lbnQiLA0KICAgICAgICAgIHBhbGV0dGUgPSAiamNvIiwgDQogICAgICAgICAgc29ydC52YWwgPSAiZGVzYyIsIA0KICAgICAgICAgIHNvcnQuYnkuZ3JvdXBzID0gVCwgDQogICAgICAgICAgYWRkID0gInNlZ21lbnRzIiwNCiAgICAgICAgICByb3RhdGUgPSBUUlVFLA0KICAgICAgICAgIGdyb3VwID0gImNvbnRpbmVudCIsDQogICAgICAgICAgZG90LnNpemUgPSA2LA0KICAgICAgICAgIGxhYmVsID0gdG9wXzMwJGdkcFBlcmNhcCwNCiAgICAgICAgICBmb250LmxhYmVsID0gbGlzdChjb2xvciA9ICJibGFjayIsIHNpemUgPSAxMiwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gLTEpLA0KICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9wdWJyKCkNCiAgICAgICAgICApDQpgYGANCg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTl9DQpnZ2RvdGNoYXJ0KHRvcF8zMCwgeCA9ICJjb3VudHJ5IiwgeSA9ICJnZHBQZXJjYXAiLA0KICAgICAgICAgIGNvbG9yID0gImNvbnRpbmVudCIsDQogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCANCiAgICAgICAgICBzb3J0aW5nID0gImRlc2MiLCANCiAgICAgICAgICBzb3J0LmJ5Lmdyb3VwcyA9IFQsIA0KICAgICAgICAgIGFkZCA9ICJzZWdtZW50cyIsDQogICAgICAgICAgcm90YXRlID0gVFJVRSwNCiAgICAgICAgICBncm91cCA9ICJjb250aW5lbnQiLA0KICAgICAgICAgIGRvdC5zaXplID0gOSwNCiAgICAgICAgICBsYWJlbCA9IHRvcF8zMCRnZHBQZXJjYXAsDQogICAgICAgICAgZm9udC5sYWJlbCA9IGxpc3QoY29sb3IgPSAiYmxhY2siLCBzaXplID0gMTIsIHZqdXN0ID0gMC41LCBoanVzdCA9IC0xKSwNCiAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfcHVicigpDQogICAgICAgICAgKQ0KYGBgDQoNCmZyb206IA0KDQpodHRwczovL3Jwa2dzLmRhdGFub3ZpYS5jb20vZ2dwdWJyL3JlZmVyZW5jZS9nZ2RvdGNoYXJ0Lmh0bWwNCmh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvYXJ0aWNsZXMvMjQtZ2dwdWJyLXB1YmxpY2F0aW9uLXJlYWR5LXBsb3RzLw0KDQpgYGB7ciBmaWcuaGVpZ2h0PTl9DQpnZ2RvdGNoYXJ0KHRvcF8zMCwgeCA9ICJjb3VudHJ5IiwgeSA9ICJnZHBQZXJjYXAiLA0KICAgICAgICAgIGNvbG9yID0gImNvbnRpbmVudCIsDQogICAgICAgICAgcGFsZXR0ZSA9ICJqY28iLCANCiAgICAgICAgICBzb3J0aW5nID0gImRlc2MiLCANCiAgICAgICAgICBzb3J0LmJ5Lmdyb3VwcyA9IFQsIA0KICAgICAgICAgIGFkZCA9ICJzZWdtZW50cyIsDQogICAgICAgICAgIyByb3RhdGUgPSBUUlVFLA0KICAgICAgICAgIGdyb3VwID0gImNvbnRpbmVudCIsDQogICAgICAgICAgZG90LnNpemUgPSA5LA0KICAgICAgICAgIGxhYmVsID0gdG9wXzMwJGdkcFBlcmNhcCwNCiAgICAgICAgICBmb250LmxhYmVsID0gbGlzdChjb2xvciA9ICJibGFjayIsIHNpemUgPSAxMiwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gLTEpLA0KICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9wdWJyKCksDQogICAgICAgICAgIyB4LnRleHQuY29sID0gVFJVRQ0KICAgICAgICAgICkNCmBgYA0KDQoNCiMgSGlnaGxpZ2h0IFBsb3RzDQoNCmBgYHtyfQ0KbGlicmFyeShnZ2hpZ2hsaWdodCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBnYXBfbG9uZ2VyKSArDQogIGdlb21fbGluZShjb2wgPSAiZ3JleSIsIGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgZ3JvdXAgPSBjb3VudHJ5KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpDQogICAgICAgICAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDE5NjAsIHRvID0gMjAyMCwgYnkgPSA1KSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBnYXBfbG9uZ2VyKSArDQogIGdlb21fbGluZShjb2wgPSAiZ3JleSIsIGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgZ3JvdXAgPSBjb3VudHJ5KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpDQogICAgICAgICAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDE5NjAsIHRvID0gMjAyMCwgYnkgPSA1KSkgKw0KICBzY2FsZV95X2xvZzEwKCkgDQpgYGANCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSANCiAgbXV0YXRlKGhpZ2hsaWdodF90eXBlID0gY2FzZV93aGVuKGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLCJEZW5tYXJrIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgZ3JvdXAgPSBjb3VudHJ5LCBjb2wgPSBoaWdobGlnaHRfdHlwZSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQ0KICAgICAgICAgICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZyb20gPSAxOTYwLCB0byA9IDIwMjAsIGJ5ID0gNSkpICsNCiAgc2NhbGVfeV9sb2cxMCgpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5IiwgImJsYWNrIikpICsNCiAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gYyguNSwgNykpDQpgYGANCg0KDQpgYGB7cn0NCmdhcF9sb25nZXIgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZW5tYXJrIiwiVW5pdGVkIFN0YXRlcyIsIlVuaXRlZCBLaW5nZG9tIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgZ3JvdXAgPSBjb3VudHJ5LCBjb2wgPSBjb3VudHJ5KSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpDQogICAgICAgICAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDE5NjAsIHRvID0gMjAyMCwgYnkgPSA1KSkgKw0KICBzY2FsZV95X2xvZzEwKCkgKyANCiAgZ2doaWdobGlnaHQoaGlnaGxpZ2h0X3R5cGUgPT0gIlllcyIpICsNCiAgbGFicyh0aXRsZSA9ICJHRFAvQ2FwaXRhIChJbmNvbWUgLyBQZXJzb24pIGluICQgb2YgQ291bnRyaWVzIHdydCB0byB0aW1lIiwNCiAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBHYXBtaW5kZXIiLA0KICAgICAgIHkgPSAiTG9nIG9mIEluY29tZSAoR0RQIC8gQ2FwaXRhKSIgDQogICAgICAgKQ0KYGBgDQoNCiMjIyBMb2cgc2NhbGUNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyfQ0KZ2FwX2xvbmdlciAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJJbmRpYSIsIlNpbmdhcG9yZSIsIk1hbGF5c2lhIiwiTm9yd2F5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAlPiUgDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShhZXMoeCA9IHllYXIsIHkgPSBnZHBQZXJjYXAsIGNvbCA9IGNvdW50cnkpLCBzaXplID0gMS4xKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDE5NjAsIHRvID0gMjAyMCwgYnkgPSA1KSkgKw0KICBzY2FsZV95X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsgDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCJncmV5IiwgMC40KSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGNvbG91ciA9ICJtYXJvb24iKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIkluY29tZShMb2cgb2YgR0RQL0NhcGl0YSBpbiAkKSBvZiB3b3JsZCBjb3VudHJpZXMgYWNyb3NzIHRpbWUiLA0KICAgICAgIHN1YnRpdGxlID0gImNyZWF0ZWQgYnkgVmlTYSIsDQogICAgICAgY2FwdGlvbiA9ICJEYXRhIFNvdXJjZTogR2FwbWluZGVyIiwNCiAgICAgICB5ID0gIkxvZyBvZiBJbmNvbWUgKEdEUCAvIENhcGl0YSkgaS5lIE11bHRpcGxpZXIgc2NhbGUiIA0KICAgICAgICkNCmBgYA0KDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9ICJnZHBQZXJDYXBfbG9nX2hpZ2hsaWdodGVkX2NoaS5qcGciLCBkcGkgPSAzMDAsIGhlaWdodCA9IDgsIHdpZHRoID0gMTApDQpgYGANCg0KDQojIyMgRmFjZXQgTG9nIHNjYWxlDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0NCmdhcF9sb25nZXIgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZW5tYXJrIiwiVW5pdGVkIFN0YXRlcyIsIlVuaXRlZCBLaW5nZG9tIiwiQ2hpbmEiKSB+ICJZZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiTm8iKSkgJT4lIA0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2wgPSBjb3VudHJ5KSwgc2l6ZSA9IDEuMSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZyb20gPSAxOTYwLCB0byA9IDIwMjAsIGJ5ID0gNSkpICsNCiAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArIA0KICBnZ2hpZ2hsaWdodChoaWdobGlnaHRfdHlwZSA9PSAiWWVzIiwNCiAgICAgICAgICAgICAgdW5oaWdobGlnaHRlZF9wYXJhbXMgPSBsaXN0KHNpemUgPSAxLCBjb2xvdXIgPSBhbHBoYSgiZ3JleSIsIDAuNCkpKSArDQogIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgY29sb3VyID0gIm1hcm9vbiIpDQogICAgICAgICAgKSArDQogIGxhYnModGl0bGUgPSAiSW5jb21lKExvZyBvZiBHRFAvQ2FwaXRhIGluICQpIG9mIHdvcmxkIGNvdW50cmllcyBiYXNlZCBvbiBjb250aW5lbnRzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJMb2cgb2YgSW5jb21lIChHRFAgLyBDYXBpdGEpIGkuZSBNdWx0aXBsaWVyIHNjYWxlIiANCiAgICAgICApDQpgYGANCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX2xvZ19oaWdobGlnaHRlZF9mYWNldF9jaGkuanBnIiwgZHBpID0gMzAwLCBoZWlnaHQgPSA4LCB3aWR0aCA9IDEwKQ0KYGBgDQoNCg0KDQojIyMgTm9ybWFsIHNjYWxlDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0NCmdhcF9sb25nZXIgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZW5tYXJrIiwiVW5pdGVkIFN0YXRlcyIsIlVuaXRlZCBLaW5nZG9tIiwiQ2hpbmEiKSB+ICJZZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiTm8iKSkgJT4lIA0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2wgPSBjb3VudHJ5KSwgc2l6ZSA9IDEuMSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZyb20gPSAxOTYwLCB0byA9IDIwMjAsIGJ5ID0gNSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IDIwMDAwMCwgYnkgPSAyNTAwMCkpICsNCiAgZ2doaWdobGlnaHQoaGlnaGxpZ2h0X3R5cGUgPT0gIlllcyIsDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoImdyZXkiLCAwLjQpKSkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgY29sb3VyID0gIm1hcm9vbiIpDQogICAgICAgICAgKSArDQogIGxhYnModGl0bGUgPSAiSW5jb21lKEdEUC9DYXBpdGEgaW4gJCkgb2Ygd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJJbmNvbWUgKEdEUCAvIENhcGl0YSkgaW4gJCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX25vcm1faGlnaGxpZ2h0ZWRfY2hpLmpwZyIsIGRwaSA9IDMwMCwgaGVpZ2h0ID0gOCwgd2lkdGggPSAxMCkNCmBgYA0KDQojIyMgRmFjZXQgTm9ybWFsIHNjYWxlDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0NCmdhcF9sb25nZXIgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEZW5tYXJrIiwiVW5pdGVkIFN0YXRlcyIsIlVuaXRlZCBLaW5nZG9tIiwiQ2hpbmEiKSB+ICJZZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiTm8iKSkgJT4lIA0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2wgPSBjb3VudHJ5KSwgc2l6ZSA9IDEuMSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKGZyb20gPSAxOTYwLCB0byA9IDIwMjAsIGJ5ID0gNSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IDIwMDAwMCwgYnkgPSAyNTAwMCkpICsNCiAgZ2doaWdobGlnaHQoaGlnaGxpZ2h0X3R5cGUgPT0gIlllcyIsDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoImdyZXkiLCAwLjQpKSkgKw0KICBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGNvbG91ciA9ICJtYXJvb24iKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIkluY29tZShHRFAvQ2FwaXRhIGluICQpIG9mIHdvcmxkIGNvdW50cmllcyBiYXNlZCBvbiBjb250aW5lbnRzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJJbmNvbWUgKEdEUCAvIENhcGl0YSkgaW4gJCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX25vcm1faGlnaGxpZ2h0ZWRfZmFjZXRfY2hpLmpwZyIsIGRwaSA9IDMwMCwgaGVpZ2h0ID0gOCwgd2lkdGggPSAxMCkNCmBgYA0KDQoNCiMjIEFwcGx5aW5nIHRoZW1lX3ZpbnlfYnJpZ2h0DQoNCg0KIyMjIExvZyBzY2FsZQ0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTJ9DQpnYXBfbG9uZ2VyICU+JSANCiAgbXV0YXRlKGhpZ2hsaWdodF90eXBlID0gY2FzZV93aGVuKGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk2MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKyANCiAgZ2doaWdobGlnaHQoaGlnaGxpZ2h0X3R5cGUgPT0gIlllcyIsDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoInBpbmsiLCAwLjQpKSkgKw0KICB0aGVtZV92aW55X2JyaWdodCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkNCiAgICAgICAgICApICsNCiAgbGFicyh0aXRsZSA9ICJJbmNvbWUoTG9nIG9mIEdEUC9DYXBpdGEgaW4gJCkgb2Ygd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJMb2cgb2YgSW5jb21lIChHRFAgLyBDYXBpdGEpIGkuZSBNdWx0aXBsaWVyIHNjYWxlIiANCiAgICAgICApDQpgYGANCg0KDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9ICJnZHBQZXJDYXBfbG9nX2hpZ2hsaWdodGVkX3BpbmsuanBnIiwgZHBpID0gMzAwLCBoZWlnaHQgPSA4LCB3aWR0aCA9IDEwKQ0KYGBgDQoNCg0KIyMjIEZhY2V0IExvZyBzY2FsZQ0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpnYXBfbG9uZ2VyICU+JSANCiAgbXV0YXRlKGhpZ2hsaWdodF90eXBlID0gY2FzZV93aGVuKGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk2MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKyANCiAgZ2doaWdobGlnaHQoaGlnaGxpZ2h0X3R5cGUgPT0gIlllcyIsDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoInBpbmsiLCAwLjQpKSkgKw0KICBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfdmlueV9icmlnaHQoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApDQogICAgICAgICAgKSArDQogIGxhYnModGl0bGUgPSAiSW5jb21lKExvZyBvZiBHRFAvQ2FwaXRhIGluICQpIG9mIHdvcmxkIGNvdW50cmllcyBiYXNlZCBvbiBjb250aW5lbnRzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJMb2cgb2YgSW5jb21lIChHRFAgLyBDYXBpdGEpIGkuZSBNdWx0aXBsaWVyIHNjYWxlIiANCiAgICAgICApDQpgYGANCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX2xvZ19oaWdobGlnaHRlZF9mYWNldF9waW5rLmpwZyIsIGRwaSA9IDMwMCwgaGVpZ2h0ID0gOCwgd2lkdGggPSAxMCkNCmBgYA0KDQoNCg0KIyMjIE5vcm1hbCBzY2FsZQ0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpnYXBfbG9uZ2VyICU+JSANCiAgbXV0YXRlKGhpZ2hsaWdodF90eXBlID0gY2FzZV93aGVuKGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk2MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSAyMDAwMDAsIGJ5ID0gMjUwMDApKSArDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCJwaW5rIiwgMC40KSkpICsNCiAgdGhlbWVfdmlueV9icmlnaHQoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApDQogICAgICAgICAgKSArDQogIGxhYnModGl0bGUgPSAiSW5jb21lKEdEUC9DYXBpdGEgaW4gJCkgb2Ygd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJJbmNvbWUgKEdEUCAvIENhcGl0YSkgaW4gJCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX25vcm1faGlnaGxpZ2h0ZWRfcGluay5qcGciLCBkcGkgPSAzMDAsIGhlaWdodCA9IDgsIHdpZHRoID0gMTApDQpgYGANCg0KIyMjIEZhY2V0IE5vcm1hbCBzY2FsZQ0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQpnYXBfbG9uZ2VyICU+JSANCiAgbXV0YXRlKGhpZ2hsaWdodF90eXBlID0gY2FzZV93aGVuKGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JSANCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IGdkcFBlcmNhcCwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk2MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSAyMDAwMDAsIGJ5ID0gMjUwMDApKSArDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCJwaW5rIiwgMC40KSkpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArDQogIHRoZW1lX3ZpbnlfYnJpZ2h0KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIkluY29tZShHRFAvQ2FwaXRhIGluICQpIG9mIHdvcmxkIGNvdW50cmllcyBiYXNlZCBvbiBjb250aW5lbnRzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJJbmNvbWUgKEdEUCAvIENhcGl0YSkgaW4gJCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWUgPSAiZ2RwUGVyQ2FwX25vcm1faGlnaGxpZ2h0ZWRfZmFjZXRfcGluay5qcGciLCBkcGkgPSAzMDAsIGhlaWdodCA9IDgsIHdpZHRoID0gMTApDQpgYGANCg0KDQoNCiMjIExvYWRpbmcgdGF4IGRhdGENCg0KIyMjIERhdGEgc291cmNlOiANCg0KaHR0cHM6Ly9vdXJ3b3JsZGluZGF0YS5vcmcvdGF4YXRpb24NCg0KDQpgYGB7cn0NCnRheF9yZXZfdG9fZ2RwIDwtIHJlYWRfY3N2KCJFOi8zLiBSL291cndvcmxkaW5kYXRhLm9yZy90b3RhbC10YXgtcmV2ZW51ZXMtZ2RwLmNzdiIpDQoNCmhlYWQodGF4X3Jldl90b19nZHApDQpgYGANCg0KYGBge3J9DQp0YXhfcmV2X3RvX2dkcCA8LSB0YXhfcmV2X3RvX2dkcCAlPiUgDQogIHJlbmFtZSgidGF4X3Jldm51ZV9wZXJjX29mX2dkcCIgPSAiVG90YWwgdGF4IHJldmVudWUgKCUgb2YgR0RQKSAoSUNURCAoMjAxOSkpIiwNCiAgICAgICAgICJjb3VudHJ5IiA9ICJFbnRpdHkiLA0KICAgICAgICAgInllYXIiID0gIlllYXIiKQ0KDQpoZWFkKHRheF9yZXZfdG9fZ2RwKQ0KYGBgDQoNCkFkZGluZyBjb250aW5lbnQNCg0KYGBge3J9DQp0YXhfcmV2IDwtIGxlZnRfam9pbih4ID0gdGF4X3Jldl90b19nZHAsDQogICAgICAgICAgeSA9IGdhcG1pbmRlciAlPiUgDQogICAgICAgICAgICBncm91cF9ieShjb3VudHJ5LCBjb250aW5lbnQpICU+JSANCiAgICAgICAgICAgIGNvdW50KCkgJT4lIA0KICAgICAgICAgICAgc2VsZWN0KGNvdW50cnksIGNvbnRpbmVudCksDQogICAgICAgICAgYnkgPSAiY291bnRyeSINCiAgICAgICAgICAgICkNCg0Kc3VtbWFyeSh0YXhfcmV2KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmF0dGFjaCh0YXhfcmV2KQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KdGF4X3JldiAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JQ0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gcm91bmQodGF4X3Jldm51ZV9wZXJjX29mX2dkcCwyKSwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk4MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfY29udGludW91cygjbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IDQwLCBieSA9IDUpKSArDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCJncmV5IiwgMC40KSkpICsNCiAgIyBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGNvbG91ciA9ICJtYXJvb24iKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFRheCBSZXZlbnVlIGVhcm5lZCAlIG9mIEdEUCBmb3Igd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJUb3RhbCBUYXggUmV2ZW51ZSAlIG9mIEdEUCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KdGF4X3JldiAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JQ0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gcm91bmQodGF4X3Jldm51ZV9wZXJjX29mX2dkcCwyKSwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk4MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9kb2xsYXIocHJlZml4ID0gIiIsIHN1ZmZpeCA9ICIgJSIpLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IDQwLCBieSA9IDUpKSArDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCJncmV5IiwgMC40KSkpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLA0KICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBjb2xvdXIgPSAibWFyb29uIikNCiAgICAgICAgICApICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBUYXggUmV2ZW51ZSBlYXJuZWQgJSBvZiBHRFAgZm9yIHdvcmxkIGNvdW50cmllcyBhY3Jvc3MgdGltZSIsDQogICAgICAgc3VidGl0bGUgPSAiY3JlYXRlZCBieSBWaVNhIiwNCiAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBHYXBtaW5kZXIiLA0KICAgICAgIHkgPSAiVG90YWwgVGF4IFJldmVudWUgJSBvZiBHRFAiIA0KICAgICAgICkNCmBgYA0KDQojIyMgQXBwbHlpbmcgdGhlbWVfdmlueV9icmlnaHQNCg0KYGBge3J9DQpsaWJyYXJ5KHNjYWxlcykNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0NCnRheF9yZXYgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJJbmRpYSIsIlNpbmdhcG9yZSIsIk1hbGF5c2lhIiwiTm9yd2F5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAlPiUNCmdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IHJvdW5kKHRheF9yZXZudWVfcGVyY19vZl9nZHAsMiksIGNvbCA9IGNvdW50cnkpLCBzaXplID0gMS4xKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbSA9IDE5ODAsIHRvID0gMjAyMCwgYnkgPSA1KSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfZG9sbGFyKHByZWZpeCA9ICIiLCBzdWZmaXggPSAiICUiKSwNCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSA2MCwgYnkgPSA1KSkgKw0KICBnZ2hpZ2hsaWdodChoaWdobGlnaHRfdHlwZSA9PSAiWWVzIiwNCiAgICAgICAgICAgICAgdW5oaWdobGlnaHRlZF9wYXJhbXMgPSBsaXN0KHNpemUgPSAxLCBjb2xvdXIgPSBhbHBoYSgicGluayIsIDAuNCkpKSArDQogICMgZmFjZXRfd3JhcCh+Y29udGluZW50KSArDQogIHRoZW1lX3ZpbnlfYnJpZ2h0KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFRheCBSZXZlbnVlIGVhcm5lZCAlIG9mIEdEUCBmb3Igd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJUb3RhbCBUYXggUmV2ZW51ZSAlIG9mIEdEUCIgDQogICAgICAgKQ0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KdGF4X3JldiAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikgfiAiWWVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JQ0KZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gcm91bmQodGF4X3Jldm51ZV9wZXJjX29mX2dkcCwyKSwgY29sID0gY291bnRyeSksIHNpemUgPSAxLjEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk4MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9kb2xsYXIocHJlZml4ID0gIiIsIHN1ZmZpeCA9ICIgJSIpLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IDYwLCBieSA9IDUpKSArDQogIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKCIjZmZhYTMzIiwgMC40KSkpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSArDQogIHRoZW1lX3ZpbnlfYnJpZ2h0KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKQ0KICAgICAgICAgICkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFRheCBSZXZlbnVlIGVhcm5lZCAlIG9mIEdEUCBmb3Igd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBTb3VyY2U6IEdhcG1pbmRlciIsDQogICAgICAgeSA9ICJUb3RhbCBUYXggUmV2ZW51ZSAlIG9mIEdEUCIgDQogICAgICAgKQ0KYGBgDQoNCiMjIHVzZXIgZGVmaW5lZCBmdW5jdGlvbg0KDQpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9DQp1ZGZfdGF4X3Jldl9wbG90IDwtIGZ1bmN0aW9uKGJhY2tncm91bmRfbGluZV9jb2xvciA9IGdyZXkpew0KICANCiAgYmFja2dyb3VuZF9saW5lX2NvbG9yID0gZW5xdW8oYmFja2dyb3VuZF9saW5lX2NvbG9yKQ0KDQogIHRheF9yZXYgJT4lIA0KICAgIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JQ0KICBnZ3Bsb3QoKSArDQogICAgZ2VvbV9saW5lKGFlcyh4ID0geWVhciwgeSA9IHJvdW5kKHRheF9yZXZudWVfcGVyY19vZl9nZHAsMiksIGNvbCA9IGNvdW50cnkpLCBzaXplID0gMS4xKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk4MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2RvbGxhcihwcmVmaXggPSAiIiwgc3VmZml4ID0gIiAlIiksDQogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSA2MCwgYnkgPSA1KSkgKw0KICAgIGdnaGlnaGxpZ2h0KGhpZ2hsaWdodF90eXBlID09ICJZZXMiLA0KICAgICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoYmFja2dyb3VuZF9saW5lX2NvbG9yLCAwLjQpKSkgKw0KICAgIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKw0KICAgIHRoZW1lX3ZpbnlfYnJpZ2h0KCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApDQogICAgICAgICAgICApICsNCiAgICBsYWJzKHRpdGxlID0gIlRvdGFsIFRheCBSZXZlbnVlIGVhcm5lZCAlIG9mIEdEUCBmb3Igd29ybGQgY291bnRyaWVzIGFjcm9zcyB0aW1lIiwNCiAgICAgICAgIHN1YnRpdGxlID0gImNyZWF0ZWQgYnkgVmlTYSIsDQogICAgICAgICBjYXB0aW9uID0gIkRhdGEgU291cmNlOiBHYXBtaW5kZXIiLA0KICAgICAgICAgeSA9ICJUb3RhbCBUYXggUmV2ZW51ZSAlIG9mIEdEUCIgDQogICAgICAgICApDQp9DQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KdWRmX3RheF9yZXZfcGxvdChwaW5rKQ0KYGBgDQoNCg0KYGBge3J9DQp1ZGZfdGF4X3Jldl9wbG90IDwtIGZ1bmN0aW9uKGJhY2tncm91bmRfbGluZV9jb2xvciA9IGdyZXkpew0KICANCiAgYmFja2dyb3VuZF9saW5lX2NvbG9yID0gZW5xdW8oYmFja2dyb3VuZF9saW5lX2NvbG9yKQ0KICANCiAgcHJpbnQoYXNfbGFiZWwoYmFja2dyb3VuZF9saW5lX2NvbG9yKSApDQoNCg0KfQ0KDQp1ZGZfdGF4X3Jldl9wbG90KCIjMzNmZmZmIikgICMzM2ZmZmYNCmBgYA0KDQpgYGB7cn0NCmF0dGFjaChnYXBtaW5kZXIpDQpgYGANCg0KDQoNCg0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KDQpjb3VudHJ5X2hpZ2hsaWdodF9wbG90IDwtIGZ1bmN0aW9uKGRmID0gZ2FwbWluZGVyLCB5X3ZhciA9IGdkcFBlcmNhcCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tncm91bmRfbGluZV9jb2xvciA9ICJncmV5IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cmllcyA9IGRlZmF1bHRfbGlzdCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKXsNCiAgDQogICMgZGVmYXVsdCBsaXN0IG9mIGhpZ2hsaWdodCBjb3VudHJpZXMNCiAgZGVmYXVsdF9saXN0ID0gYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpDQogIA0KICAjIHF1b3RpbmcgeS1heGlzIHZhcmlhYmxlDQogIHlfdmFyID0gZW5xdW8oeV92YXIpDQoNCiAgIyBEYXRhIFByZXAuDQogIGRmICU+JSANCiAgICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lIGNvdW50cmllcyB+ICJZZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vIikpICU+JQ0KICAjIFBsb3R0aW5nICANCiAgZ2dwbG90KCkgKw0KICAgIGdlb21fbGluZShhZXMoeCA9IHllYXIsIHkgPSByb3VuZCghIXlfdmFyLDIpLCBjb2wgPSBjb3VudHJ5KSwgc2l6ZSA9IDEuMSkgKw0KICAgIA0KICAgICMgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tID0gMTk4MCwgdG8gPSAyMDIwLCBieSA9IDUpKSArDQogICAgIyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfZG9sbGFyKHByZWZpeCA9ICIiLCBzdWZmaXggPSAiICUiKSwNCiAgICAjICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IDAsIHRvID0gNjAsIGJ5ID0gNSkpICsNCiAgICANCiAgICBnZ2hpZ2hsaWdodChoaWdobGlnaHRfdHlwZSA9PSAiWWVzIiwNCiAgICAgICAgICAgICAgICB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3Qoc2l6ZSA9IDEsIGNvbG91ciA9IGFscGhhKGJhY2tncm91bmRfbGluZV9jb2xvciwgMC40KSkpICsNCiAgICAjIGZhY2V0X3dyYXAofmNvbnRpbmVudCkgKw0KICAgIA0KICAgIHRoZW1lX3ZpbnlfYnJpZ2h0KCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApDQogICAgICAgICAgICApICsNCiAgICANCiAgICBsYWJzKHRpdGxlID0gIkdEUC9DYXAgZm9yIHdvcmxkIGNvdW50cmllcyBhY3Jvc3MgdGltZSIsDQogICAgICAgICBzdWJ0aXRsZSA9ICJjcmVhdGVkIGJ5IFZpU2EiLA0KICAgICAgICAgY2FwdGlvbiA9ICJEYXRhIFNvdXJjZTogR2FwbWluZGVyIiwNCiAgICAgICAgIHkgPSAiVG90YWwgVGF4IFJldmVudWUgJSBvZiBHRFAiIA0KICAgICAgICAgKQ0KfQ0KDQpjb3VudHJ5X2hpZ2hsaWdodF9wbG90KGRmID0gZ2FwX2xvbmdlciwgY291bnRyaWVzID0gYygiQXVzdHJhbGlhIiwiU2luZ2Fwb3JlIiksIGJhY2tncm91bmRfbGluZV9jb2xvciA9ICJwaW5rIikNCmBgYA0KDQojIyBnZ2NoYXJ0cyBsaWIgZm9yIGR1bWJlbGwgcGxvdCANCg0KZ2V0dGluZyBpc3N1ZXMgd2l0aCB0aGlzDQoNCmBgYHtyfQ0KbGlicmFyeShnZ2NoYXJ0cykNCmBgYA0KDQoNCmBgYHtyfQ0KaGVhZChnYXBtaW5kZXJfbmV3KQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmRhdGEoInBvcGV1cm9wZSIpDQpgYGANCg0KYGBge3J9DQpoZWFkKHBvcGV1cm9wZSkNCmBgYA0KDQpgYGB7cn0NCnBvcGV1cm9wZSAlPiUgDQogIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikpICU+JSANCiAgZ2djaGFydHM6OmR1bWJiZWxsX2NoYXJ0KHggPSBjb3VudHJ5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeTEgPSBwb3AxOTUyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeTIgPSBwb3AyMDA3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24gPSAxMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50X2NvbG9ycyA9IGMoImxpZ2h0Z3JheSIsIm9yYW5nZSIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArDQogICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGltaXRzID0gYygwLCBOQSksDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUoeCwgIk1uLiIpDQogICkgKw0KICB0aGVtZV9uZygpDQpgYGANCg0KYGBge3J9DQpkdW1iYmVsbF9jaGFydCgNCiAgZGF0YSA9IHBvcGV1cm9wZSwNCiAgeCA9IGNvdW50cnksDQogIHkxID0gcG9wMTk1MiwNCiAgeTIgPSBwb3AyMDA3LA0KICB0b3BfbiA9IDEwLA0KICBwb2ludF9jb2xvcnMgPSBjKCJsaWdodGdyYXkiLCAiIzQ5NEY1QyIpDQopICsNCiAgbGFicygNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gIlBvcHVsYXRpb24iLA0KICAgIHRpdGxlID0gIkV1cm9wZSdzIExhcmdlc3QgQ291bnRyaWVzIGJ5IFBvcHVsYXRpb24gaW4gMjAwNyINCiAgKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsaW1pdHMgPSBjKDAsIE5BKSwNCiAgICBsYWJlbHMgPSBmdW5jdGlvbih4KSBwYXN0ZSh4LCAiTW4uIikNCiAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSANCiAgc2VsZWN0KGNvdW50cnksICIyMDEwIiwgIjIwMTkiKSAlPiUgaGVhZCgpIA0KYGBgDQoNCg0KDQojIyBnZ2FsdCBmb3IgZHVtYmVsbCBwbG90cw0KDQpgYGB7cn0NCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdiYmMvYmJwbG90JykNCg0KbGlicmFyeShnZ2FsdCkNCmxpYnJhcnkoYmJwbG90KQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSANCiAgc2VsZWN0KGNvdW50cnksICIyMDEwIiwgIjIwMTkiKSAlPiUgDQogIG11dGF0ZShnYXAgPSAiMjAxOSIgLSAiMjAxMCIpICU+JSBoZWFkKCkgDQpgYGANCg0KYGBge3J9DQpnYXBtaW5kZXIgJT4lDQogIGZpbHRlcih5ZWFyID09IDE5NjcgfCB5ZWFyID09IDIwMDcpICU+JQ0KICBzZWxlY3QoY291bnRyeSwgeWVhciwgbGlmZUV4cCkgJT4lDQogIHNwcmVhZCh5ZWFyLCBsaWZlRXhwKSAlPiUNCiAgbXV0YXRlKGdhcCA9IGAyMDA3YCAtIGAxOTY3YCkgJT4lDQogIGFycmFuZ2UoZGVzYyhnYXApKSAlPiUNCiAgaGVhZCgxMCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ldyAlPiUgDQogIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiSW5kaWEiLCJTaW5nYXBvcmUiLCJNYWxheXNpYSIsIk5vcndheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAiRGVubWFyayIsIlVuaXRlZCBTdGF0ZXMiLCJVbml0ZWQgS2luZ2RvbSIsIkNoaW5hIikpICU+JSANCiAgc2VsZWN0KGNvdW50cnksICIyMDEwIiwgIjIwMTkiKSAlPiUgDQogIG11dGF0ZShnYXAgPSAuW1siMjAxOSJdXSAtIC5bWyIyMDEwIl1dKSAlPiUNCiAgYXJyYW5nZShkZXNjKGdhcCkpICU+JQ0KICANCiAgZ2dwbG90KGFlcyh4ID0gLltbIjIwMTAiXV0sIHhlbmQgPSAuW1siMjAxOSJdXSwgDQogICAgICAgICB5ID0gcmVvcmRlcihjb3VudHJ5LCBnYXApLCBncm91cCA9IGNvdW50cnkpKSArDQogIGdlb21fZHVtYmJlbGwoY29sb3VyID0gIiNkZGRkZGQiLCBzaXplID0gMiwNCiAgICAgICAgICAgICAgICBjb2xvdXJfeCA9ICJncmV5IiwgY29sb3VyX3hlbmQgPSAiI0ZBQUIxOCIpICsNCiAgYmJjX3N0eWxlKCkNCmBgYA0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSAlaW4lIGMoIkluZGlhIiwiU2luZ2Fwb3JlIiwiTWFsYXlzaWEiLCJOb3J3YXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpKSAlPiUgDQogIHNlbGVjdChjb3VudHJ5LCAiMTk3MCIsICIyMDE5IikgJT4lIA0KICBtdXRhdGUoZ2FwID0gLltbIjIwMTkiXV0gLSAuW1siMTk3MCJdXSkgJT4lIA0KICBhcnJhbmdlKGRlc2MoZ2FwKSkgJT4lDQoNCiAgZ2dwbG90KGFlcyh4ID0gLltbIjE5NzAiXV0sIHhlbmQgPSAuW1siMjAxOSJdXSwNCiAgICAgICAgIHkgPSByZW9yZGVyKGNvdW50cnksIGdhcCksIGdyb3VwID0gY291bnRyeSkpICsNCiAgZ2VvbV9kdW1iYmVsbChjb2xvdXIgPSAiI2RkZGRkZCIsIHNpemUgPSAyLA0KICAgICAgICAgICAgICAgIGNvbG91cl94ID0gImdyZXkiLCBjb2xvdXJfeGVuZCA9ICIjRkFBQjE4IikgKw0KICBiYmNfc3R5bGUoKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChnYXBfbG9uZ2VyKQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JQ0KICBncm91cF9ieShjb250aW5lbnQpICU+JSANCiAgZmlsdGVyKGNvdW50cnkgPT0gdG9wX24oeD0uLG49NSx3dD1nZHBQZXJjYXApICU+JSBzZWxlY3QoY291bnRyeSkpDQpgYGANCg0KQmVsb3cgY29kZSBnaXZlcyB0b3AgNSBjb3VudHJpZXMgYnkgZ2RwUGVyY2FwIGFjcm9zcyB0aGUgeWVhcnMgDQoNCmBgYHtyfQ0KT3ZlcmFsbF90b3A1IDwtIGdhcF9sb25nZXIgJT4lIA0KICBncm91cF9ieShjb250aW5lbnQsIGNvdW50cnkpICU+JSANCiAgc3VtbWFyaXNlKGdkcFBlcmNhcCA9IG1heChnZHBQZXJjYXAsIG5hLnJtID0gVCkpICU+JSANCiAgdG9wX24obiA9IDUsIHd0ID0gZ2RwUGVyY2FwKSAlPiUgDQogIHVuZ3JvdXAoKQ0KDQpPdmVyYWxsX3RvcDUNCmBgYA0KDQoNCkJlbG93IGNvZGUgZ2l2ZXMgdG9wIDUgY291bnRyaWVzIGJ5IGdkcFBlcmNhcCAgb2YgZWFjaCBjb250aW5lbnQgYWNyb3NzIHRoZSB5ZWFycyANCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSANCiAgZmlsdGVyKA0KICAgICAgICAgY291bnRyeSAlaW4lIChPdmVyYWxsX3RvcDUgJT4lIHB1bGwoY291bnRyeSkpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBnZHBQZXJjYXAsIGNvbG9yID0gY291bnRyeSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfdmlueV9icmlnaHQoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpDQoNCmBgYA0KDQpgYGB7cn0NCk92ZXJhbGxfdG9wNSAlPiUgc2VsZWN0KGNvdW50cnkpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ2FwX2xvbmdlciAlPiUgDQogIGZpbHRlcih5ZWFyID09IDIwMTksDQogICAgICAgICBjb3VudHJ5ICVpbiUgKE92ZXJhbGxfdG9wNSAlPiUgcHVsbChjb3VudHJ5KSkpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ2FwX2xvbmdlciAlPiUgDQogIGZpbHRlcih5ZWFyID09IDIwMTksDQogICAgICAgICBjb3VudHJ5ICVpbiUgT3ZlcmFsbF90b3A1JGNvdW50cnkgKSANCg0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSANCiAgZmlsdGVyKA0KICAgIHllYXIgPT0gMjAxOSwNCiAgICBjb3VudHJ5ICVpbiUgKE92ZXJhbGxfdG9wNSAlPiUgcHVsbChjb3VudHJ5KSkNCiAgKQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSAyMDE5LCBjb3VudHJ5ICVpbiUgT3ZlcmFsbF90b3A1KSANCmBgYA0KDQpgYGB7cn0NCmdhcF9sb25nZXIgJT4lIA0KICAgIGZpbHRlcih5ZWFyID09IDIwMTksIGNvdW50cnkgJWluJSBPdmVyYWxsX3RvcDUpICU+JSANCiAgICBwdWxsKGNvdW50cnkpDQpgYGANCg0KYGBge3J9DQpnYXBfbG9uZ2VyICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSAyMDE5LCBjb3VudHJ5ICVpbiUgT3ZlcmFsbF90b3A1JGNvdW50cnkpICU+JSANCiAgICBzZWxlY3QoY291bnRyeSkNCmBgYA0KDQoNCmBgYHtyfQ0KdG9wNV9ieV9jb250aW5lbnRzIDwtIGdhcF9sb25nZXIgJT4lIA0KICAgIGZpbHRlcih5ZWFyID09IDIwMTksIGNvdW50cnkgJWluJSBPdmVyYWxsX3RvcDUkY291bnRyeSkgJT4lIA0KICAgIHB1bGwoY291bnRyeSkNCg0KdG9wNV9ieV9jb250aW5lbnRzDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQ0KZ2FwX2xvbmdlciAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUgdG9wNV9ieV9jb250aW5lbnRzIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAgJT4lIA0KICAjZmlsdGVyKCFpcy5uYShjb250aW5lbnQpKSAlPiUgZmlsdGVyKCFpcy5uYShnZHBQZXJjYXApKSAlPiUgI2hlYWQoKQ0KICBuYS5leGNsdWRlICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwLCBjb2wgPSBjb3VudHJ5KSwgc2l6ZSA9IDEuMSkgKw0KICBnZ2hpZ2hsaWdodChoaWdobGlnaHRfdHlwZSA9PSAiWWVzIiwgDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoImdyZXkiLCAwLjQpKSkgKw0KICBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCnRheF9yZXYgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJJbmRpYSIsIlNpbmdhcG9yZSIsIk1hbGF5c2lhIiwiTm9yd2F5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAlPiUNCiAgZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSB5ZWFyLCB5ID0gdGF4X3Jldm51ZV9wZXJjX29mX2dkcCwgY29sID0gYXMuZmFjdG9yKGNvdW50cnkpKSwgc2l6ZSA9IDEuMSkgKw0KICBnZ2hpZ2hsaWdodChoaWdobGlnaHRfdHlwZSA9PSAiWWVzIiwgDQogICAgICAgICAgICAgIHVuaGlnaGxpZ2h0ZWRfcGFyYW1zID0gbGlzdChzaXplID0gMSwgY29sb3VyID0gYWxwaGEoImdyZXkiLCAwLjQpKSkgKw0KICBmYWNldF93cmFwKH5jb250aW5lbnQpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2FwX2xvbmdlciAlPiUgDQogIG11dGF0ZShoaWdobGlnaHRfdHlwZSA9IGNhc2Vfd2hlbihjb3VudHJ5ICVpbiUgdG9wNV9ieV9jb250aW5lbnRzIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAgJT4lIA0KICAjZmlsdGVyKCFpcy5uYShjb250aW5lbnQpKSAlPiUgZmlsdGVyKCFpcy5uYShnZHBQZXJjYXApKSAlPiUgI2hlYWQoKQ0KICBuYS5leGNsdWRlICU+JSBzdW1tYXJ5KCkNCmBgYA0KDQpgYGB7cn0NCnRheF9yZXYgJT4lIA0KICBtdXRhdGUoaGlnaGxpZ2h0X3R5cGUgPSBjYXNlX3doZW4oY291bnRyeSAlaW4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJJbmRpYSIsIlNpbmdhcG9yZSIsIk1hbGF5c2lhIiwiTm9yd2F5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRlbm1hcmsiLCJVbml0ZWQgU3RhdGVzIiwiVW5pdGVkIEtpbmdkb20iLCJDaGluYSIpIH4gIlllcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJObyIpKSAlPiUNCiAgc3VtbWFyeSgpDQpgYGANCg0KDQojIHBsb3RseSBoaWdobGlnaHQgY2hhcnRzDQoNCmZyb206IA0KDQpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy82NDM4NTI0MC9pcy10aGVyZS1hLXdheS10by1oYXZlLWEtaGlnaGxpZ2h0ZWQtY2hhcnQtYXMtd2VsbC1hcy1oYXZlLWludGVyYWN0aXZpdHktb2Ytc2VsZT9ub3JlZGlyZWN0PTEjY29tbWVudDExMzg1NjgwMV82NDM4NTI0MA0KDQpodHRwczovL3Bsb3RseS1yLmNvbS9jbGllbnQtc2lkZS1saW5raW5nLmh0bWwjZmlsdGVyDQoNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQpgYGANCg0KDQpgYGB7cn0NCiMgZnJvbSBnZ3Bsb3QyIGxpYnJhcnkNCg0KZGF0YSgidHhob3VzaW5nIikNCmBgYA0KDQoNCmBgYHtyfQ0KdHhob3VzaW5nICU+JSBnbGltcHNlKCkNCmBgYA0KDQpgYGB7cn0NCnR4aG91c2luZyAlPiUgIGhlYWQoKQ0KYGBgDQoNCg0KYGBge3J9DQp0eCA8LSBoaWdobGlnaHRfa2V5KHR4aG91c2luZywgfmNpdHkpDQpgYGANCg0KYGBge3J9DQpiYXNlIDwtIHBsb3RfbHkodHgsIGNvbG9yID0gSSgiZ3JleSIpKSAlPiUgDQogIGdyb3VwX2J5KGNpdHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KdGltZV9zZXJpZXMgPC0gYmFzZSAlPiUgDQogICAgICAgICAgICAgICAgICBncm91cF9ieShjaXR5KSAlPiUgDQogICAgICAgICAgICAgICAgICBhZGRfbGluZXMoeCA9IH5kYXRlLCB5ID0gfm1lZGlhbikNCmBgYA0KDQoNCmBgYHtyfQ0KaGlnaGxpZ2h0KA0KICB0aW1lX3NlcmllcywNCiAgb24gPSAicGxvdGx5X2hvdmVyIiwNCiAgc2VsZWN0aXplID0gRkFMU0UsDQogIGR5bmFtaWMgPSBGQUxTRSwNCiAgY29sb3IgPSAicmVkIiwNCiAgcGVyc2lzdGVudCA9IEZBTFNFDQopDQpgYGANCg0KDQpgYGB7cn0NCnBsb3RseV8xIDwtIGdhcF9sb25nZXIgJT4lIA0KICBmaWx0ZXIoY29udGluZW50ID09ICJBc2lhIikgJT4lIA0KICBuYS5leGNsdWRlKCkgJT4lIA0KICANCiAgcGxvdGx5OjpoaWdobGlnaHRfa2V5KC4sIH5jb3VudHJ5KQ0KYGBgDQoNCg0KYGBge3J9DQpiYXNlIDwtIHBsb3RfbHkocGxvdGx5XzEsIGNvbG9yID0gSSgiZ3JleSIpKSAlPiUgDQogIGdyb3VwX2J5KGNvdW50cnkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KdGltZV9zZXJpZXMgPC0gYmFzZSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KGNvdW50cnkpICU+JSANCiAgICAgICAgICAgICAgYWRkX2xpbmVzKHggPSB+eWVhciwgeSA9IH5nZHBQZXJjYXApDQpgYGANCg0KDQpgYGB7cn0NCmhpZ2hsaWdodCgNCiAgdGltZV9zZXJpZXMsDQogIG9uID0gInBsb3RseV9ob3ZlciIsDQogIHNlbGVjdGl6ZSA9IEZBTFNFLA0KICBkeW5hbWljID0gRkFMU0UsDQogIGNvbG9yID0gInJlZCIsDQogIHBlcnNpc3RlbnQgPSBGQUxTRQ0KKQ0KYGBgDQoNCg0KYGBge3J9DQpwbG90bHlfMSA8LSBnYXBfbG9uZ2VyICU+JSANCiAgZmlsdGVyKGNvbnRpbmVudCA9PSAiQXNpYSIsIGdkcFBlcmNhcCA8IG1heCgyMDAwMCkpICU+JSANCiAgbmEuZXhjbHVkZSgpICU+JSANCiAgDQogIHBsb3RseTo6aGlnaGxpZ2h0X2tleSguLCB+Y291bnRyeSkNCmBgYA0KDQoNCmBgYHtyfQ0KYmFzZSA8LSBwbG90X2x5KHBsb3RseV8xLCBjb2xvciA9IEkoImdyZXkiKSkgJT4lIA0KICBncm91cF9ieShjb3VudHJ5KQ0KDQpgYGANCg0KDQpgYGB7cn0NCnRtZV9zZXJpZXMgPC0gYmFzZSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KGNvdW50cnkpICU+JSANCiAgICAgICAgICAgICAgYWRkX2xpbmVzKHggPSB+eWVhciwgeSA9IH5nZHBQZXJjYXApDQpgYGANCg0KDQpgYGB7cn0NCmhpZ2hsaWdodCgNCiAgdGltZV9zZXJpZXMsDQogIG9uID0gInBsb3RseV9jbGljayIsDQogIHNlbGVjdGl6ZSA9IFRSVUUsDQogIGR5bmFtaWMgPSBUUlVFLA0KICBjb2xvciA9ICJyZWQiLA0KICBwZXJzaXN0ZW50ID0gVFJVRQ0KKQ0KYGBgDQoNCmBgYHtyfQ0KaGlzdCA8LSBhZGRfaGlzdG9ncmFtKA0KICBiYXNlLA0KICB4ID0gfmdkcFBlcmNhcCwNCiAgaGlzdG5vcm0gPSAicHJvYmFiaWxpdHkgZGVuc2l0eSINCikNCg0Kc3VicGxvdCh0aW1lX3NlcmllcywgaGlzdCwgbnJvd3MgPSAyKSAlPiUgDQogIGxheW91dChiYXJtb2RlID0gIm92ZXJsYXkiLCBzaG93bGVnZW5kID0gRkFMU0UpICU+JSANCiAgaGlnaGxpZ2h0KA0KICAgIGR5bmFtaWMgPSBUUlVFLA0KICAgIHNlbGVjdGl6ZSA9IFRSVUUsDQogICAgY29sb3IgPSAicmVkIiwNCiAgICBzZWxlY3RlZCA9IGF0dHJzX3NlbGVjdGVkKG9wYWNpdHkgPSAwLjMpDQogICkNCg0KYGBgDQoNCg0KDQoNCg0KIyMjIFRyeWluZyB0byBzdWJ0cmFjdCByb3cgd2lzZQ0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkNCmBgYA0KDQojIyMjIGZhaWxlZCBhdHRlbXB0cw0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkgJT4lIA0KICAuW1siSW5kaWEiLF1dIC0gLltbIkJhbmdsYWRlc2giLF1dDQpgYGANCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSANCiAgZmlsdGVyKGNvdW50cnkgPT0gYygiSW5kaWEiLCJCYW5nbGFkZXNoIikpICU+JSANCiAgLlsiSW5kaWEiLF0gLSAuWyJCYW5nbGFkZXNoIixdDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkgJT4lIA0KICAuW2NvdW50cnkgPT0gIkluZGlhIixdIC0gLltjb3VudHJ5ID09ICJCYW5nbGFkZXNoIixdDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkgJT4lIA0KICAuW0luZGlhLF0gLSAuW0JhbmdsYWRlc2gsXQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSANCiAgZmlsdGVyKGNvdW50cnkgPT0gYygiSW5kaWEiLCJCYW5nbGFkZXNoIikpICU+JSANCiAgLltbSW5kaWEsXV0gLSAuW1tCYW5nbGFkZXNoLF1dDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkgJT4lIA0KICBtdXRhdGUoRGlmZl9yZXN1bHQgPSAoLiAlPiUgZmlsdGVyKGNvdW50cnkgPT0gIkluZGlhIikgKSAtICguICU+JSBmaWx0ZXIoY291bnRyeSA9PSAiQmFuZ2xhZGVzaCIpKSAgKQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ld1tjb3VudHJ5ID09ICJJbmRpYSIsXSAtIGdhcG1pbmRlcl9uZXdbY291bnRyeSA9PSAiQmFuZ2xhZGVzaCIsXQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ld1tJbmRpYSxdIC0gZ2FwbWluZGVyX25ld1tCYW5nbGFkZXNoLF0NCmBgYA0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXcgJT4lIA0KICBmaWx0ZXIoY291bnRyeSA9PSAiSW5kaWEiKQ0KYGBgDQoNCmBgYHtyfQ0Kcm93bmFtZXMoZ2FwbWluZGVyX25ld1tjb3VudHJ5ID09ICJJbmRpYSIsXSkNCmBgYA0KDQpgYGB7cn0NCnJvd25hbWVzKGdhcG1pbmRlcl9uZXdbY291bnRyeSA9PSBJbmRpYSxdKQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3W2NvdW50cnkgPT0gIkluZGlhIixdDQpgYGANCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3DQpgYGANCg0KDQpgYGB7cn0NCnJvd25hbWVzKGdhcG1pbmRlcl9uZXcpIDwtIGdhcG1pbmRlcl9uZXckY291bnRyeQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ldyAlPiUgIGhlYWQoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ld1siSW5kaWEiLCAyOiBuY29sKGdhcG1pbmRlcl9uZXcpXSAjLSBnYXBtaW5kZXJfbmV3WyJCYW5nbGFkZXNoIiwgXQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ld1ssIDI6bmNvbChnYXBtaW5kZXJfbmV3KV0NCmBgYA0KDQojIyMjIHNvbHV0aW9uIGZyb20gc3RhY2tvdmVyZmxvdw0KDQpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy82NDQxMDk2Ni9ob3ctdG8tc3VidHJhY3QtYS1yb3ctZnJvbS1hbm90aGVyLXJvdy1pbi1yDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25ldyAlPiUgDQogIGZpbHRlcihjb3VudHJ5ICVpbiUgYygiSW5kaWEiLCJCYW5nbGFkZXNoIikpICU+JSANCiAgc2VsZWN0KC0xKSAlPiUgDQogIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+bGVhZCgueCkgLSAoLngpKSkgJT4lIG5hLm9taXQoKQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSBzZWxlY3QoLTEpICU+JSBtdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfmxlYWQoLngpIC0gKC54KSkpICU+JSBuYS5vbWl0KCkNCmBgYA0KDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmV3ICU+JSANCiAgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJJbmRpYSIsIkJhbmdsYWRlc2giKSkNCmBgYA0KDQojIyMjIE15IG93biBzb2x1dGlvbg0KDQpgYGB7cn0gDQp0ZXN0X3NldCA8LSBhcy5kYXRhLmZyYW1lKGdhcG1pbmRlcl9uZXcpDQoNCnJvd25hbWVzKHRlc3Rfc2V0KSA8LSB0ZXN0X3NldCRjb3VudHJ5DQoNCmhlYWQodGVzdF9zZXQpDQpgYGANCg0KYGBge3J9DQp0ZXN0X3NldCAlPiUgIHNlbGVjdCgtY291bnRyeSkNCg0KdGVzdF9zZXRbIkluZGlhIiwyOm5jb2wodGVzdF9zZXQpXSAtIHRlc3Rfc2V0WyJCYW5nbGFkZXNoIiwyOm5jb2wodGVzdF9zZXQpXQ0KYGBgDQoNCiMjIyBzZXR0aW5nIHRvb2xpcHMgLyBoaWdoY2hhcnRlcg0KDQpmcm9tOiBodHRwczovL2prdW5zdC5jb20vYmxvZy9wb3N0cy8yMDE5LTAyLTA0LXVzaW5nLXRvb2x0aXBzLWluLXVuZXhwZWN0ZWQtd2F5cy8NCg0KYGBge3J9DQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQ0KYGBgDQoNCiMjIyMgU2VyaWVzIGNoYXJ0DQoNCmBgYHtyfQ0KZ3AgPC0gZ2FwbWluZGVyICU+JSANCiAgYXJyYW5nZShkZXNjKHllYXIpKSAlPiUgDQogIGRpc3RpbmN0KGNvdW50cnksIC5rZWVwX2FsbCA9IFRSVUUpDQoNCmhlYWQoZ3ApDQpgYGANCg0KDQpgYGB7cn0NCmdwMiA8LSBnYXBtaW5kZXIgJT4lIA0KICBzZWxlY3QoY291bnRyeSwgeWVhciwgcG9wKSAlPiUgDQogIG5lc3QoLWNvdW50cnkpICU+JSANCiAgbXV0YXRlKA0KICAgIGRhdGEgPSBtYXAoZGF0YSwgbXV0YXRlX21hcHBpbmcsIGhjYWVzKHggPSB5ZWFyLCB5ID0gcG9wKSwgZHJvcCA9IFRSVUUpLA0KICAgIGRhdGEgPSBtYXAoZGF0YSwgbGlzdF9wYXJzZSkNCiAgKSAlPiUgDQogIHJlbmFtZSh0dGRhdGEgPSBkYXRhKQ0KDQpncDINCmBgYA0KDQoNCmBgYHtyfQ0KZ3B0b3QgPC0gbGVmdF9qb2luKGdwLCBncDIsIGJ5ID0gImNvdW50cnkiKQ0KDQpoZWFkKGdwdG90KQ0KYGBgDQoNCmBgYHtyfQ0KaGNoYXJ0KA0KICBncHRvdCwNCiAgInBvaW50IiwNCiAgaGNhZXMoeCA9IGxpZmVFeHAsIHkgPSBnZHBQZXJjYXAsIG5hbWUgPSBjb3VudHJ5LCANCiAgICAgICAgc2l6ZT1wb3AsIGdyb3VwPWNvbnRpbmVudCwgbmFtZT1jb3VudHJ5KQ0KKSAlPiUgDQogIA0KICBoY195QXhpcyh0eXBlID0gImxvZ2FyaXRobWljIikgJT4lIA0KICANCiAgIyB0aGlzIGlzIHdoYXQgY3JlYXRlcyBhIGNoYXJ0IGluIHRvb2x0aXANCiAgaGNfdG9vbHRpcCgNCiAgICB1c2VIVE1MID0gVFJVRSwNCiAgICBoZWFkZXJGb3JtYXQgPSAiPGI+e3BvaW50LmtleX08L2I+IiwNCiAgICBwb2ludEZvcm1hdHRlciA9IHRvb2x0aXBfY2hhcnQoYWNjZXNvciA9ICJ0dGRhdGEiKQ0KICApICU+JSANCiAgDQogIGhjX3RpdGxlKHRleHQgPSAiVG9vbHRpcCBjaGFydCB3aXRoaW4gYSBDaGFydCIpICU+JSANCiAgaGNfc3VidGl0bGUodGV4dCA9ICIodHRjaGFydDogcG9wdWxhdGlvbiBncm93dGgpIikNCmBgYCAgDQogIA0KIyMjIyMgbG9nDQoNCmBgYHtyfQ0KZ3AyX2xvZyA8LSBnYXBtaW5kZXIgJT4lIA0KICBzZWxlY3QoY291bnRyeSwgeWVhciwgcG9wKSAlPiUgDQogIG5lc3QoLWNvdW50cnkpICU+JSANCiAgbXV0YXRlKA0KICAgIGRhdGEgPSBtYXAoZGF0YSwgbXV0YXRlX21hcHBpbmcsIGhjYWVzKHggPSB5ZWFyLCB5ID0gbG9nKHBvcCkpLCBkcm9wID0gVFJVRSksDQogICAgZGF0YSA9IG1hcChkYXRhLCBsaXN0X3BhcnNlKQ0KICApICU+JSANCiAgcmVuYW1lKHR0ZGF0YV9sb2cgPSBkYXRhKQ0KDQpncDJfbG9nDQpgYGANCg0KDQpgYGB7cn0NCmdwdG90X2xvZyA8LSBsZWZ0X2pvaW4oZ3AsIGdwMl9sb2csIGJ5ID0gImNvdW50cnkiKQ0KDQpoZWFkKGdwdG90X2xvZykNCmBgYA0KDQoNCmBgYHtyfQ0KaGNoYXJ0KA0KICBncHRvdF9sb2csDQogICJwb2ludCIsDQogIGhjYWVzKHggPSBsaWZlRXhwLCB5ID0gZ2RwUGVyY2FwLCBuYW1lID0gY291bnRyeSwgDQogICAgICAgIHNpemU9cG9wLCBncm91cD1jb250aW5lbnQsIG5hbWU9Y291bnRyeSkNCikgJT4lIA0KICANCiAgaGNfeUF4aXModHlwZSA9ICJsb2dhcml0aG1pYyIpICU+JSANCiAgDQogICMgdGhpcyBpcyB3aGF0IGNyZWF0ZXMgYSBjaGFydCBpbiB0b29sdGlwDQogIGhjX3Rvb2x0aXAoDQogICAgdXNlSFRNTCA9IFRSVUUsDQogICAgaGVhZGVyRm9ybWF0ID0gIjxiPntwb2ludC5rZXl9PC9iPiIsDQogICAgcG9pbnRGb3JtYXR0ZXIgPSB0b29sdGlwX2NoYXJ0KGFjY2Vzb3IgPSAidHRkYXRhX2xvZyIpDQogICkgJT4lIA0KICANCiAgaGNfdGl0bGUodGV4dCA9ICJUb29sdGlwIGNoYXJ0IHdpdGhpbiBhIENoYXJ0IikgJT4lIA0KICBoY19zdWJ0aXRsZSh0ZXh0ID0gIih0dGNoYXJ0OiBsb2cgcG9wdWxhdGlvbiBncm93dGgpIikNCmBgYCANCg0KIyMjIyBkb251dCBjaGFydA0KDQpmcm9tOiBodHRwczovL2prdW5zdC5jb20vYmxvZy9wb3N0cy8yMDE5LTAyLTA0LXVzaW5nLXRvb2x0aXBzLWluLXVuZXhwZWN0ZWQtd2F5cy8NCg0KYGBge3J9DQpkb251dGRhdGEgPC0gZ3AgJT4lIA0KICBncm91cF9ieShjb250aW5lbnQpICU+JSANCiAgc3VtbWFyaXNlKHBvcCA9IHN1bShwb3AvMWU2KSAqIDFlNikNCg0KZG9udXRkYXRhDQpgYGANCg0KYGBge3J9DQpoY2hhcnQoZG9udXRkYXRhLCAicGllIiwNCiAgICAgICBoY2FlcyhuYW1lID0gY29udGluZW50LCB5ID0gcG9wKSwgaW5uZXJTaXplID0gMzAwKQ0KYGBgDQoNCg0KYGBge3J9DQpkb251dGRhdGEyIDwtIA0KICBncCAlPiUgDQogIHNlbGVjdChjb250aW5lbnQsIGxpZmVFeHAsIGdkcFBlcmNhcCkgJT4lIA0KICBuZXN0KC1jb250aW5lbnQpICU+JSANCiAgbXV0YXRlKA0KICAgIGRhdGEgPSBtYXAoZGF0YSwgbXV0YXRlX21hcHBpbmcsIGhjYWVzKHggPSBsaWZlRXhwLCB5ID0gZ2RwUGVyY2FwKSwgZHJvcCA9IFRSVUUpLA0KICAgIGRhdGEgPSBtYXAoZGF0YSwgbGlzdF9wYXJzZSkNCiAgKSAlPiUgDQogIHJlbmFtZSh0dGRhdGEgPSBkYXRhKSAlPiUNCiAgbGVmdF9qb2luKGRvbnV0ZGF0YSkNCg0KZG9udXRkYXRhMg0KYGBgDQoNCmBgYHtyfQ0KaGMgPC0gaGNoYXJ0KA0KICBkb251dGRhdGEyLCAicGllIiwgaGNhZXMobmFtZSA9IGNvbnRpbmVudCwgeSA9IHBvcCksDQogIGlubmVyU2l6ZSA9IDM3NQ0KKQ0KDQpoYw0KYGBgDQoNCg0KYGBge3J9DQpoYyAlPiUgDQogIGhjX3Rvb2x0aXAoDQogICAgdXNlSFRNTCA9IFRSVUUsDQogICAgaGVhZGVyRm9ybWF0ID0gIjxiPntwb2ludC5rZXl9PC9iPiIsDQogICAgcG9pbnRGb3JtYXR0ZXIgPSB0b29sdGlwX2NoYXJ0KA0KICAgICAgYWNjZXNvciA9ICJ0dGRhdGEiLA0KICAgICAgaGNfb3B0cyA9IGxpc3QoDQogICAgICAgIGNoYXJ0ID0gbGlzdCh0eXBlID0gInNjYXR0ZXIiKSwNCiAgICAgICAgY3JlZGl0cyA9IGxpc3QoZW5hYmxlZCA9IEZBTFNFKSwNCiAgICAgICAgcGxvdE9wdGlvbnMgPSBsaXN0KHNjYXR0ZXIgPSBsaXN0KG1hcmtlciA9IGxpc3QocmFkaXVzID0gMikpKQ0KICAgICAgICApLA0KICAgICAgaGVpZ2h0ID0gMjI1DQogICAgICApLA0KICAgIHBvc2l0aW9uZXIgPSBKUygNCiAgICAgICJmdW5jdGlvbiAoKSB7DQogICAgICANCiAgICAgICAgLyogb25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwYXJ0cyEgKi8NCiAgICAgICAgeHAgPSAgdGhpcy5jaGFydC5jaGFydFdpZHRoLzIgLSB0aGlzLmxhYmVsLndpZHRoLzINCiAgICAgICAgeXAgPSAgdGhpcy5jaGFydC5jaGFydEhlaWdodC8yIC0gdGhpcy5sYWJlbC5oZWlnaHQvMg0KICAgICAgDQogICAgICAgIHJldHVybiB7IHg6IHhwLCB5OiB5cCB9Ow0KICAgICAgDQogICAgICB9IiksDQogICAgc2hhZG93ID0gRkFMU0UsDQogICAgYm9yZGVyV2lkdGggPSAwLA0KICAgIGJhY2tncm91bmRDb2xvciA9ICJ0cmFuc3BhcmVudCIsDQogICAgaGlkZURlbGF5ID0gMTAwMA0KICApDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K